It's definitely possible but not in the way you think, at least from what I understood from your post.
Just adding rows to _ModuleVersion and _ModuleVersionFile is also not enough as neither DownloadServer nor GatewayServer have received information about this.
nToBePacked just means the files are supposed to go into a PackFile (.pk2)
You might be confusing this with the required compression used by SMC which is zLib in vSRO and lzma in some other clients.
You can either write your own launcher that downloads from a cdn based on a "version.txt" file, thus never modifying the actual silkroad version but importing the files into pk2.
Or you can write a clientless tool that acts like SMC. Meaning its communicating via GlobalManager to automate the patching.
Step 0. Fix your SecurityApi
There are some oversights in @

's

. They only happen in backend communication, that's why they went unnoticed.
0x1001 is supposed to be ignored from CRC and Sequence.
Code:
// Original
byte sb1 = GenerateCountByte(true);
// Fixed
byte sb1 = opcode == 0x1001 ? (byte)0 : GenerateCountByte(true);
// Original
byte sb2 = GenerateCheckByte(writer.GetBytes());
// Fixed
byte sb2 = opcode == 0x1001 ? (byte)0 : GenerateCheckByte(writer.GetBytes());
// Original
byte expected_count = GenerateCountByte(true);
// Fixed
byte expected_count = packet_opcode == 0x1001 ? (byte)0 : GenerateCountByte(true);
// Original
byte expected_crc = GenerateCheckByte(buffer.Buffer);
// Fixed
byte expected_crc = packet_opcode == 0x1001 ? (byte)0 : GenerateCheckByte(buffer.Data);
The FormatPacket is out of order thus messing with CRC and Sequence. I'm not sure if this one is 100% needed, but just in case.
Code:
// Original
ushort parts = 0;
PacketWriter final = new PacketWriter();
PacketWriter final_data = new PacketWriter();
byte[] input_data = packet.GetBytes();
PacketReader input_reader = new PacketReader(input_data);
TransferBuffer workspace = new TransferBuffer(4089, 0, input_data.Length);
while (workspace.Size > 0)
{
PacketWriter part_data = new PacketWriter();
int cur_size = workspace.Size > 4089 ? 4089 : workspace.Size; // Max buffer size is 4kb for the client
part_data.Write((byte)0); // Data flag
part_data.Write(input_data, workspace.Offset, cur_size);
workspace.Offset += cur_size;
workspace.Size -= cur_size; // Update the size
final_data.Write(FormatPacket(0x600D, part_data.GetBytes(), false));
++parts; // Track how many parts there are
}
// Write the final header packet to the front of the packet
PacketWriter final_header = new PacketWriter();
final_header.Write((byte)1); // Header flag
final_header.Write((short)parts);
final_header.Write(packet.Opcode);
final.Write(FormatPacket(0x600D, final_header.GetBytes(), false));
Code:
// Fixed
ushort parts = 0;
PacketWriter final = new PacketWriter();
PacketWriter final_data = new PacketWriter();
byte[] input_data = packet.GetBytes();
PacketWriter header = new PacketWriter();
header.Write((byte)1); //Header flag
header.Write((ushort)((input_data.Length / 4089) + (input_data.Length % 4089 != 0 ? 1 : 0)));
header.Write(packet.Opcode);
final.Write(this.FormatPacket(0x600D, header.GetBytes(), false));
TransferBuffer workspace = new TransferBuffer(4089, 0, input_data.Length);
while (workspace.Length > 0)
{
PacketWriter part_data = new PacketWriter();
int cur_size = workspace.Length > 4089 ? 4089 : workspace.Length; // Max buffer size is 4kb for the client
part_data.Write((byte)0); // Data flag
part_data.Write(input_data, workspace.Offset, cur_size);
workspace.Offset += cur_size;
workspace.Length -= cur_size; // Update the size
final_data.Write(this.FormatPacket(0x600D, part_data.GetBytes(), false));
++parts; // Track how many parts there are
}
Step 1.
Login as an SMC that has the required SecurityDescriptions
0x7001 - SM_LOGIN_REQ
Code:
2 ushort Username.Length
* string Username
2 ushort PasswordHash.Length
* string PasswordHash //MD5
2 ushort SSNumber2Hash.Length
* string SSNumber2Hash //MD5, can be empty
4 uint ProtocolVersion //24
0xB001 - SM_LOGIN_ACK
Code:
1 byte result
if(result == 1)
{
1 byte curentSecurityGroup
1 byte currentDivision
// SecurityDescriptionData (optional)
// See https://github.com/DummkopfOfHachtenduden/SilkroadDoc/wiki/0xA003-FRAMEWORK_MSG_CERTIFICATION_ACK.md
}
else if(result == 0x02)
{
1 byte errorCode
//01 = cannot connect to division manager (wrong version)
//02 = cannot connect to division manager (invalid user info)
//03 = cannot connect to division manager (server internal error)
//04 = cannot connect to division manager (access denied)
}
Step 2.
Request the architecture info (optional)
0x7002 - SM_ARCHITECTURE_REQ
0xB002 - SM_ARCHITECTURE_ACK
Code:
See https://github.com/DummkopfOfHachtenduden/SilkroadDoc/wiki/0xA003-FRAMEWORK_MSG_CERTIFICATION_ACK.md
Step 3.
Request module version info
0x7003 - SM_MODULE_VERSION_REQ
0xB003 - SM_MODULE_VERSION_ACK
Code:
1 byte result
while(true)
{
1 byte entryFlag
if(entryFlag == 0)
break;
4 uint nID
1 byte nDivisionID
1 byte nContentID
1 byte nModuleID
4 uint nVersion
64 string szVersion
256 string szDesc
1 byte nValid //Always 1 as GlobalManager culls all obsolete versions
}
Step 4.
Prepare patch on the front servers [GatewayServer(s) and DownloadServer(s)]
GlobalManager will give you the latest version info so you can compare your local files against it.
0x7104 - SM_MODULE_PATCH_PREPARE_REQ
Code:
1 byte ModuleID // SR_Client or ServiceManager
1 byte ContentID
0xB104 - SM_MODULE_PATCH_PREPARE_ACK
Code:
1 byte result
if(result == 0x01)
{
//ModuleVersion@HEAD
while(true)
{
1 byte entryFlag
if(entryFlag == 0)
break;
4 uint nID
1 byte nDivisionID
1 byte nContentID
1 byte nModuleID
4 uint nVersion
64 string szVersion
256 string szDesc
1 byte nValid // always 1 as GlobalManager culls all obsolete versions
}
while(true)
{
1 byte entryFlag
if(entryFlag == 0)
break;
4 uint nID
4 uint nVersion
1 byte nDivisionID
1 byte nContentID
1 byte nModuleID
256 string szFilename
256 string szPath
4 uint nFileSize
1 byte nFileType
4 uint nFileTypeVersion //1001 part of JMXV*****
1 byte nToBePacked
2 ushort timeModified.Year
2 ushort timeModified.Month
2 ushort timeModified.Day
2 ushort timeModified.Hour
2 ushort timeModified.Minute
2 ushort timeModified.Second
4 uint timeModified.Microsecond
1 byte nValid
}
}
else if(result == 0x02)
{
1 byte errorCode
//01 = "cannot prepare patch : another user patch in progress"
//02 = "cannot prepare patch : 5:DownloadServer is offline"
//03 = "cannot prepare patch : DB error"
//?? = "cannot prepare patch : front server prepare patch fail"
}
Step 5.
Compress your workspace with zLib. C#s DeflateStream only works for decompression.
The zLib from the

works fine
Step 6.
Make an upload request to GlobalManager.
0x7100 - SM_MODULE_PATCH_UPLOAD_REQ (Massive)
Code:
4 uint nID
1 byte nDivisionID
1 byte nContentID
1 byte nModuleID
1 uint nVersion
64 string szVersion
256 string szDesc
1 byte nValid // always 1 as GlobalManager culls all obsolete versions
2 ushort fileCount
foreach(file)
{
4 uint nID
4 uint nVersion
1 byte nDivisionID
1 byte nContentID
1 byte nModuleID
256 string szFilename
256 string szPath
4 uint nFileSize
1 byte nFileType
4 uint nFileTypeVersion //1001 part of JMXV*****
1 byte nToBePacked
2 ushort timeModified.Year
2 ushort timeModified.Month
2 ushort timeModified.Day
2 ushort timeModified.Hour
2 ushort timeModified.Minute
2 ushort timeModified.Second
4 uint timeModified.Microsecond
1 byte nValid
}
0xB100 - SM_MODULE_PATCH_UPLOAD_ACK
Step 7.
GlobalManager will request the the files that are supposed to be uploaded via 0x6004 - FRAMEWORK_FILE_TRANSFER_REQ
Code:
4 uint file.ID
4 uint fileOffset // Used to continue interupted downloads from last chunk. (can be ignored)
You're now supposed to send 0x1001 - NETENGINE_FILE_DATA
Code:
* byte[] data // up to 4090 bytes of raw file data
until the file is complete then you send 0xA004 - FRAMEWORK_FILE_TRANSFER_ACK
Code:
1 byte result
if(result == 0x02)
{
1 byte errorCode
}
GlobalManager will also send 0x3101 - SM_MODULE_PATCH_NOTIFY_PROGRESS telling you what's going on. (optional)
Code:
1 byte notifyFlag //1 = Relay, 2 = Recording database
if(notifyFlag & 1)
{
1 byte state // 1 = Start, 2 = Progress
if(state == 2)
{
1 byte fileCount
1 byte fileCurrent
}
}
if(notifyFlag & 2)
{
1 byte state // 1 = Start, 2 = Progress
if(state == 2)
{
1 byte progress // 0-100
}
}
Repeat Step 7. until GlobalManager sends 0x3102 - SM_MODULE_PATCH_NOTIFY_COMPLETE
Code:
1 byte result
if(result == 2)
{
1 byte errorCode
// 1 = patch failed: relay server offline
// 2 = patch failed: fail to tranfer patch data to relay server
// 3 = patch failed: service operating
// 4 = patch failed: recording db failed
}
Step 8. Since you're automating stuff make sure you put the front servers back into service
0x7008 - SM_CONTROL_SERVICE_START_REQ
Code:
1 byte ServiceMode // 1 = Start, 2 = Stop
2 ushort ServerBodyID
Your users can now download the latest patches via Silkroad.exe as usual.
If you have questions just ask.