Hello everyone,
I think nobody knows me, but I'm a member of the Underground here. Since I will resign from hacking S4, I want to make my hacks public to share with everyone. I stopped playing S4 League long ago, so I don't care much about S4League anymore.
What I want to share with you is the decryption of the S4League resource files. It is also used for decrypting the network traffic, but you have to figure that out yourself.
Ok, lets start with the resource.s4hd file. It contains the internal file names and the crc value of the decrypted/uncompressed file and it's compressed and encrypted by a key.
The key is
Only the first 32 values are used. They use the LZO compression library.
The filename in the _resources folder is the crc as hex value. The files are compressed and encrypted with the same key.
Files that end with .x4 are special. They are compressed and encrypted again, but with the whole 40 byte key above. X4 files contain many parameters of the game, like height of jump, gravity, prices for shop items and many more...
AFAIK, the CRC is not checked when reading the files from disk. The CRC is the CRC32 of the internal name and the CRC32 of the uncompressed/decrypted file. Both are encrypted with the key above. They really know how to reuse code. :D
I add the source code of a program to this post that contains the functions for decrypting the resource files. To prevent easy leaching, you have to figure out how to use it yourself. But I'm sure, someone will make a pretty filebrowser for this game. :D Oh, and you need the miniLZO files for compiling.
If you have an older installation of S4League that was patched several times, the clean resource function is very useful. It deletes all unused files from the _resources folder, which saved me 1.5 GB disk space.
Code like this has been around for months, so we already had our fun, now they can fix it. :D I bet they need around 2 months to fix it and everyone has to download the full game again. I give no support for this code and I will not fix it, when they change something. I'm out of the game.
Now the code:
Edit: Let me add the code as C#:
The GUI Software has been removed. DON'T ASK ME FOR IT! Thanks. Ask someone of the leechers, who already downloaded it. Or write it yourself.
I think nobody knows me, but I'm a member of the Underground here. Since I will resign from hacking S4, I want to make my hacks public to share with everyone. I stopped playing S4 League long ago, so I don't care much about S4League anymore.
What I want to share with you is the decryption of the S4League resource files. It is also used for decrypting the network traffic, but you have to figure that out yourself.
Ok, lets start with the resource.s4hd file. It contains the internal file names and the crc value of the decrypted/uncompressed file and it's compressed and encrypted by a key.
The key is
Code:
0x82, 0x53, 0x43, 0x4C, 0x2B, 0x0D, 0x37, 0xD7, 0xD9, 0xD8, 0x1B, 0x6D, 0xA0, 0xC3, 0x2B, 0xEE, 0x45, 0x88, 0x1A, 0xA6, 0x18, 0x1D, 0x9D, 0x38, 0x2A, 0x55, 0x03, 0x1D, 0xCD, 0xA6, 0x73, 0x07, 0xED, 0x8D, 0xC5, 0xDB, 0xA3, 0xBD, 0xB6, 0xD5
The filename in the _resources folder is the crc as hex value. The files are compressed and encrypted with the same key.
Files that end with .x4 are special. They are compressed and encrypted again, but with the whole 40 byte key above. X4 files contain many parameters of the game, like height of jump, gravity, prices for shop items and many more...
AFAIK, the CRC is not checked when reading the files from disk. The CRC is the CRC32 of the internal name and the CRC32 of the uncompressed/decrypted file. Both are encrypted with the key above. They really know how to reuse code. :D
I add the source code of a program to this post that contains the functions for decrypting the resource files. To prevent easy leaching, you have to figure out how to use it yourself. But I'm sure, someone will make a pretty filebrowser for this game. :D Oh, and you need the miniLZO files for compiling.
If you have an older installation of S4League that was patched several times, the clean resource function is very useful. It deletes all unused files from the _resources folder, which saved me 1.5 GB disk space.
Code like this has been around for months, so we already had our fun, now they can fix it. :D I bet they need around 2 months to fix it and everyone has to download the full game again. I give no support for this code and I will not fix it, when they change something. I'm out of the game.
Now the code:
Code:
#include "windows.h"
#include "minilzo.203\minilzo.h"
class DecryptUtils {
private:
BYTE static const Key[];
DWORD *CRCTable;
protected:
DWORD* readDWORD(BYTE** pointer) {
DWORD* p = (DWORD*)*pointer;
(*pointer)+=4;
return p;
}
bool decryptX4(DWORD size, BYTE * data)
{
DWORD i = 0;
while ( i < size )
{
*data = ((signed int)*data >> 1) & 0x7F | (unsigned __int8)(((*data & 1) << 7) & 0x80);
*data++ ^= Key[i++ % 40];
}
return true;
}
bool encryptX4(DWORD size, BYTE* data) {
DWORD i = 0;
while ( i < size )
{
*(BYTE *)data ^= Key[i % 40];
*(BYTE *)data = (unsigned __int8)(2 * *(BYTE *)data & 0xFE) | ((*(BYTE *)data & 0x80) >> 7) & 1;
++data;
++i;
}
return true;
}
bool decryptCapped256(DWORD size, BYTE* data) {
int sizeCapped;
int i = 0;
if ( size >= 256 )
sizeCapped = 256;
else
sizeCapped = size;
while ( i < sizeCapped )
{
*data = ((signed int)*data >> 1) & 0x7F | (unsigned __int8)(((*data & 1) << 7) & 0x80);
*data++ ^= Key[i++ % 32];
}
return true;
}
bool encryptCapped256(DWORD size, BYTE* data) {
int sizeCapped;
int i = 0;
if ( size >= 256 )
sizeCapped = 256;
else
sizeCapped = size;
while ( i < sizeCapped )
{
*(BYTE *)data ^= Key[i % 32];
*(BYTE *)data = (unsigned __int8)(2 * *(BYTE *)data & 0xFE) | ((*(BYTE *)data & 0x80) >> 7) & 1;
++data;
++i;
}
return true;
}
int compress(DWORD sizeIN, BYTE* dataIN, DWORD* sizeOUT, BYTE* dataOUT) {
BYTE* dict = (BYTE*)malloc(0x10000);
return lzo1x_1_compress(dataIN, sizeIN, dataOUT, sizeOUT, dict);
}
int decompress(DWORD sizeIN, BYTE* dataIN, DWORD* sizeOUT, BYTE* dataOUT) {
return lzo1x_decompress_safe(dataIN, sizeIN, dataOUT, sizeOUT, NULL);
}
bool swapBytes(DWORD size, BYTE *data) {
int sizeCapped;
int i = 0;
BYTE swap;
int j;
if ( size >= 128 )
sizeCapped = 128;
else
sizeCapped = size;
while ( i < sizeCapped / 2 )
{
j = size - 1 - i;
swap = data[j];
data[j] = data[i];
data[i++] = swap;
}
return true;
}
DWORD crc32(BYTE *data, DWORD length)
{
register unsigned long crc;
unsigned long i;
crc = 0xFFFFFFFF;
for (i = 0; i < length; i++)
{
crc = ((crc >> 8) & 0x00FFFFFF) ^ CRCTable[(crc ^ *data++) & 0xFF];
}
return (crc ^ 0xFFFFFFFF);
}
void crc32gentab ()
{
unsigned long crc, poly;
int i, j;
CRCTable = new DWORD[256];
poly = 0xEDB88320L;
for (i = 0; i < 256; i++) {
crc = i;
for (j = 8; j > 0; j--) {
if (crc & 1) {
crc = (crc >> 1) ^ poly;
} else {
crc >>= 1;
}
}
CRCTable[i] = crc;
}
}
};
struct FolderFileHeader {
char FileName[256];
unsigned __int64 CRC;
DWORD FileSize;
DWORD Unknown;
};
struct Buffer {
DWORD Size;
BYTE* Data;
};
class Folder : public DecryptUtils {
private:
static const char* Name;
FILE* Handle;
int Size;
BYTE* Data;
bool Initialized;
BYTE* Pointer;
BYTE* FileStart;
DWORD* Version;
DWORD* NumberOfFiles;
FolderFileHeader* FileHeader;
bool Decrypted;
void DecryptFolder();
Buffer OpenFileCRC(char* crc, DWORD realSize);
Buffer DecryptX4(Buffer data);
public:
Folder();
~Folder();
void List();
Buffer OpenFile(char * name);
__int64 GenerateCRC(Buffer data, char * name);
void CleanResources();
};
const BYTE DecryptUtils::Key[] = { // Only the first 40 keys copied from S4
0x82, 0x53, 0x43, 0x4C, 0x2B, 0x0D, 0x37, 0xD7,
0xD9, 0xD8, 0x1B, 0x6D, 0xA0, 0xC3, 0x2B, 0xEE,
0x45, 0x88, 0x1A, 0xA6, 0x18, 0x1D, 0x9D, 0x38,
0x2A, 0x55, 0x03, 0x1D, 0xCD, 0xA6, 0x73, 0x07,
0xED, 0x8D, 0xC5, 0xDB, 0xA3, 0xBD, 0xB6, 0xD5
};
const char* Folder::Name = "resource.s4hd";
Folder::Folder() {
this->Initialized = false;
this->Decrypted = false;
// Open Resource File
int errorNumber = fopen_s(&(this->Handle), this->Name, "rb");
if (errorNumber != 0 || this->Handle == NULL) {
printf("Error reading file %s. ErrorNr: %u\n", this->Name, errorNumber);
//return errorNumber;
return;
}
fseek(this->Handle, 0, SEEK_END);
this->Size = ftell(this->Handle);
rewind(this->Handle);
if (this->Size < 8) {
printf("File %s must have at least 8 bytes in size.\n", this->Name);
fclose(this->Handle);
return;
}
// Read Resource File into memory
this->Data = (BYTE*) malloc(this->Size);
if (this->Data == NULL) {
printf("Could not alloc memory of size %u bytes.\n", this->Size);
}
int readBytes = fread_s(this->Data, this->Size, 1, this->Size, this->Handle);
if (readBytes != this->Size) {
printf("Expected to read %u bytes, but read %u bytes.\n", this->Size, readBytes);
fclose(this->Handle);
free(this->Data);
return;
}
// Read Version and number of Files;
this->Pointer = this->Data;
this->Version = readDWORD(&this->Pointer);
if (*(this->Version) != 1) {
printf("First DWORD of %s has to be 1, Pentavision changed the format.\n", this->Name);
fclose(this->Handle);
free(this->Data);
return;
}
this->NumberOfFiles = readDWORD(&this->Pointer);
this->FileStart = this->Pointer;
this->Initialized = true;
DecryptFolder();
}
Folder::~Folder() {
if (this->Initialized && this->Handle != NULL) {
fclose(this->Handle);
free(this->Data);
}
}
void Folder::DecryptFolder() {
if (!this->Initialized) return;
DWORD fileCounter = *this->NumberOfFiles;
BYTE* endOfFile = this->Data + this->Size;
this->FileHeader = new FolderFileHeader[fileCounter];
int i = 0;
while (fileCounter >= 0 && this->Pointer < endOfFile) {
DWORD * headerBlockSize = readDWORD(&this->Pointer);
if (*headerBlockSize + this->Pointer > endOfFile) {
printf("Length of header block size bigger than available memory. Size is %u at %u.\n", *headerBlockSize, this->Pointer - this->Data - sizeof(DWORD));
return;
}
decryptCapped256(*headerBlockSize, this->Pointer);
swapBytes(*headerBlockSize, this->Pointer);
DWORD headerSize = sizeof(FolderFileHeader);
decompress(*headerBlockSize, this->Pointer, &headerSize, (BYTE*)&this->FileHeader[i]);
decryptCapped256(headerSize, (BYTE*)&this->FileHeader[i]);
swapBytes(headerSize, (BYTE*)&this->FileHeader[i]);
this->Pointer+= *headerBlockSize;
fileCounter--;
i++;
}
this->Decrypted = true;
}
void Folder::List() {
if (!this->Initialized || !this->Decrypted) return;
for (DWORD i = 0; i < *this->NumberOfFiles; ++i) {
printf("Filename: %s \nFile-CRC: %I64X\nReal File-Size: %u\nUnknown: %X\n", this->FileHeader[i].FileName, this->FileHeader[i].CRC, this->FileHeader[i].FileSize, this->FileHeader[i].Unknown);
}
}
Buffer Folder::OpenFile(char *name) {
Buffer ret;
ret.Data = 0;
ret.Size = 0;
if (!this->Initialized || !this->Decrypted) return ret;
for (DWORD i = 0; i < *this->NumberOfFiles; ++i) {
if (strncmp(name, this->FileHeader[i].FileName, 256) == 0) {
char* fileName = new char[29];
sprintf_s(fileName, 29, "_resources\\%I64X", this->FileHeader[i].CRC);
Buffer data = OpenFileCRC(fileName, this->FileHeader[i].FileSize);
if (data.Data==0) return data;
Buffer ret = data;
if (strstr(this->FileHeader[i].FileName, ".x4")!=0) {
ret = DecryptX4(data);
free(data.Data);
}
delete fileName;
return ret;
}
}
return ret;
}
Buffer Folder::DecryptX4(Buffer data) {
Buffer x4;
x4.Size = *((DWORD*)data.Data);
data.Data+=4;
data.Size-=4;
x4.Data = (BYTE*) malloc(x4.Size);
if (x4.Data == NULL) {
printf("Could not alloc memory of size %u bytes.\n", x4.Size);
}
decryptX4(data.Size, data.Data);
decompress(data.Size, data.Data, &x4.Size, x4.Data);
data.Data -= 4;
return x4;
}
Buffer Folder::OpenFileCRC(char *crc, DWORD realSize) {
FILE* handle;
Buffer d;
d.Data = 0;
d.Size = 0;
int errNo = fopen_s(&handle, crc, "rb");
if (errNo != 0 || handle == NULL) {
printf("Error reading file %s. ErrorNr: %u\n", crc, errNo);
return d;
}
fseek(handle, 0, SEEK_END);
d.Size = ftell(handle);
rewind(handle);
d.Data = (BYTE*) malloc(d.Size);
if (d.Data == NULL) {
printf("Could not alloc memory of size %u bytes.\n", d.Size);
}
int readBytes = fread_s(d.Data, d.Size, 1, d.Size, handle);
if (readBytes != d.Size) {
printf("Expected to read %u bytes, but read %u bytes.\n", d.Size, readBytes);
fclose(handle);
free(d.Data);
return d;
}
swapBytes(d.Size, d.Data);
Buffer real;
real.Size = realSize;
real.Data = (BYTE*) malloc(real.Size);
if (real.Data == NULL) {
printf("Could not alloc memory of size %u bytes.\n", real.Size);
}
decompress(d.Size, d.Data, &real.Size, real.Data);
decryptCapped256(real.Size, real.Data);
free(d.Data);
fclose(handle);
return real;
}
__int64 Folder::GenerateCRC(Buffer data, char* name) {
crc32gentab();
__int64 dataCRC = crc32(data.Data, data.Size);
__int64 nameCRC = crc32((BYTE*)name, strlen(name));
__int64 crc = dataCRC | (nameCRC << 32);
encryptCapped256(8, (BYTE*)&crc);
return crc;
}
void Folder::CleanResources() {
if (!this->Initialized || !this->Decrypted) return;
int e = system("dir _resources >nul");
if (e != 0) return;
rename("_resources", "_resources.bkp");
system("mkdir _resources");
for (DWORD i = 0; i < *this->NumberOfFiles; ++i) {
char* oldName = new char[100];
sprintf_s(oldName, 100, "_resources.bkp\\%I64X", this->FileHeader[i].CRC);
char* newName = new char[100];
sprintf_s(newName, 100, "_resources\\%I64X", this->FileHeader[i].CRC);
rename(oldName, newName);
delete oldName;
delete newName;
}
printf("You can now remove the folder _resources.bkp from the S4League Folder.\n");
}
int main(int argc, char* argv[])
{
if (argc <= 1) {
printf("Usage:\n");
printf("program.exe list \t\t\t\t\tList files\n");
printf("program.exe clean \t\t\t\t\tClean resources\n");
printf("program.exe extract filename \t\t\t\tExtract file\n");
printf("program.exe crc external_file internal_filename \tGenerate CRC\n");
}
if (argc == 2) {
if (strcmp("list", argv[1]) == 0) {
Folder* folder = new Folder();
folder->List();
}
if (strcmp("clean", argv[1]) == 0) {
Folder* folder = new Folder();
folder->CleanResources();
}
}
if (argc == 3) {
if (strcmp("extract", argv[1])==0) {
Folder* folder = new Folder();
Buffer file = folder->OpenFile(argv[2]);
printf("%s",file.Data);
}
}
if (argc == 4) {
if (strcmp("crc", argv[1])==0) {
Folder* folder = new Folder();
FILE* handle;
Buffer d;
d.Data = 0;
d.Size = 0;
int errNo = fopen_s(&handle, argv[2], "rb");
if (errNo != 0 || handle == NULL) {
printf("Error reading file %s. ErrorNr: %u\n", argv[2], errNo);
return 1;
}
fseek(handle, 0, SEEK_END);
d.Size = ftell(handle);
rewind(handle);
d.Data = (BYTE*) malloc(d.Size);
if (d.Data == NULL) {
printf("Could not alloc memory of size %u bytes.\n", d.Size);
}
int readBytes = fread_s(d.Data, d.Size, 1, d.Size, handle);
if (readBytes != d.Size) {
printf("Expected to read %u bytes, but read %u bytes.\n", d.Size, readBytes);
fclose(handle);
free(d.Data);
return 1;
}
__int64 crc = folder->GenerateCRC(d, argv[3]);
printf("CRC: %I64X", crc);
}
}
return 0;
}
Edit: Let me add the code as C#:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Fesersoft.Hashing;
namespace S4Resource
{
class ZipFolder
{
#region Dll-Imports
[DllImport("lzo.dll")]
private static extern int __lzo_init3();
[DllImport("lzo.dll")]
private static extern string lzo_version_string();
[DllImport("lzo.dll")]
private static extern string lzo_version_date();
[DllImport("lzo.dll")]
private static extern int lzo1x_1_compress(
byte[] src,
int src_len,
byte[] dst,
ref int dst_len,
byte[] wrkmem
);
[DllImport("lzo.dll")]
private static extern int lzo1x_decompress(
byte[] src,
int src_len,
byte[] dst,
ref int dst_len,
byte[] wrkmem);
#endregion
private byte[] _workMemory = new byte[16384L * 4];
private class FileHeader
{
public string FileName;
public long CRC;
public int FileSize;
public int Unknown;
}
private bool initialized = false;
private bool decrypted = false;
private BinaryReader fileReader;
private String fileName;
private int numberOfFiles;
private Dictionary<String, FileHeader> fileHeaders;
private static byte[] Key = { // Only the first 40 keys copied from S4
0x82, 0x53, 0x43, 0x4C, 0x2B, 0x0D, 0x37, 0xD7,
0xD9, 0xD8, 0x1B, 0x6D, 0xA0, 0xC3, 0x2B, 0xEE,
0x45, 0x88, 0x1A, 0xA6, 0x18, 0x1D, 0x9D, 0x38,
0x2A, 0x55, 0x03, 0x1D, 0xCD, 0xA6, 0x73, 0x07,
0xED, 0x8D, 0xC5, 0xDB, 0xA3, 0xBD, 0xB6, 0xD5
};
private static char[] trimChars = { '\0' };
private string workingPath;
public ZipFolder(string filename)
{
int init = __lzo_init3();
if (init != 0)
{
throw new Exception("Initialization of LZO-Compressor failed !");
}
this.fileName = filename;
if (!File.Exists(filename)) return;
if (!Directory.Exists("S4Resource.backup"))
{
Directory.CreateDirectory("S4Resource.backup");
}
workingPath = Directory.GetParent(Application.ExecutablePath).FullName;
Directory.SetCurrentDirectory(workingPath);
byte[] data;
try
{
data = File.ReadAllBytes(filename);
}
catch (Exception e)
{
MessageBox.Show("Can not open file " + filename + "\nException: " + e.Message);
return;
}
if (data.Length < 8)
{
MessageBox.Show("File too small!");
return;
}
fileReader = new BinaryReader(new MemoryStream(data));
int fileVersion = fileReader.ReadInt32();
if (fileVersion != 1)
{
MessageBox.Show("File format has changed!");
return;
}
numberOfFiles = fileReader.ReadInt32();
initialized = true;
decryptHeaders();
}
private void decryptHeaders()
{
if (!initialized) return;
fileHeaders = new Dictionary<String, FileHeader>(numberOfFiles);
int fileCounter = numberOfFiles;
while (fileCounter > 0)
{
int blockSize = fileReader.ReadInt32();
byte[] header = fileReader.ReadBytes(blockSize);
decryptCapped256(ref header, header.Length);
swapBytes(ref header, header.Length);
header = Decompress(header,272);
decryptCapped256(ref header, 272);
swapBytes(ref header, 272);
BinaryReader headerReader = new BinaryReader(new MemoryStream(header));
FileHeader realHeader = new FileHeader();
realHeader.FileName = Encoding.ASCII.GetString(headerReader.ReadBytes(256)).Trim(trimChars);
realHeader.CRC = headerReader.ReadInt64();
realHeader.FileSize = headerReader.ReadInt32();
realHeader.Unknown = headerReader.ReadInt32();
fileHeaders.Add(realHeader.FileName, realHeader);
fileCounter--;
}
decrypted = true;
}
public List<String> getFileNames()
{
if (!initialized || !decrypted) return new List<String>();
return fileHeaders.Keys.ToList<String>();
}
private void swapBytes(ref byte[] data, int size)
{
int sizeCapped;
int i = 0;
byte swap;
int j;
if (size >= 128)
sizeCapped = 128;
else
sizeCapped = size;
while (i < sizeCapped / 2)
{
j = size - 1 - i;
swap = data[j];
data[j] = data[i];
data[i++] = swap;
}
}
private void decryptCapped256(ref byte[] data, int size)
{
int sizeCapped;
int i = 0;
if (size >= 256)
sizeCapped = 256;
else
sizeCapped = size;
while (i < sizeCapped)
{
data[i] = (byte)(((data[i] >> 1) & 0x7F) | ((data[i] & 1) << 7));
data[i] ^= Key[i % 32];
i++;
}
}
private void encryptCapped256(ref byte[] data, int size)
{
int sizeCapped;
int i = 0;
if (size >= 256)
sizeCapped = 256;
else
sizeCapped = size;
while (i < sizeCapped)
{
data[i] ^= Key[i % 32];
data[i] = (byte)(((data[i] & 0x7F) << 1) | ((data[i] & 0x80) >> 7));
i++;
}
}
internal void replaceFile(String fullPath, ref byte[] fileData, bool correctCRC)
{
if (!initialized || !decrypted) return;
Directory.SetCurrentDirectory(workingPath);
FileHeader fileHeader = fileHeaders[fullPath];
byte[] data = fileData;
if (fullPath.EndsWith(".x4"))
{
data = encryptX4(fileData);
}
if (fileHeader.FileName == null)
{
fileHeader.FileName = fullPath;
fileHeader.CRC = GenerateCRC(fullPath, ref data);
fileHeader.FileSize = fileData.Length;
fileHeader.Unknown = 0;
fileHeaders.Add(fileHeader.FileName, fileHeader);
}
else
{
fileHeader.FileSize = data.Length;
if (correctCRC)
{
fileHeader.CRC = GenerateCRC(fileHeader.FileName, ref data);
}
}
if (File.Exists("_resources\\" + fileHeader.CRC.ToString("X")))
{
File.Copy("_resources\\" + fileHeader.CRC.ToString("X"), "S4Resource.backup\\" + DateTime.Now.Year + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + "_" + +DateTime.Now.Hour + "-" + DateTime.Now.Minute + "-" + DateTime.Now.Second + "_" + fileHeader.CRC.ToString("X"));
}
saveEncryptedFile(fileHeader, ref data);
saveFolder();
}
private byte[] encryptX4(byte[] fileData)
{
byte[] compressed = Compress(fileData);
encrypt(ref compressed, compressed.Length);
byte[] final = new byte[compressed.Length + 4];
BinaryWriter br = new BinaryWriter(new MemoryStream(final));
br.Write(fileData.Length);
br.Write(compressed);
br.Close();
return final;
}
private void encrypt(ref byte[] data, int size)
{
int i = 0;
while (i < size)
{
data[i] ^= Key[i % 40];
data[i] = (byte)(((data[i] & 0x7F) << 1) | ((data[i] & 0x80) >> 7));
++i;
}
}
private void decrypt(ref byte[] data, int size)
{
int i = 0;
while (i < size)
{
data[i] = (byte)(((data[i] >> 1) & 0x7F) | ((data[i] & 1) << 7));
data[i] ^= Key[i % 40];
++i;
}
}
private void saveEncryptedFile(FileHeader fileHeader, ref byte[] fileData)
{
if (!initialized || !decrypted) return;
encryptCapped256(ref fileData, fileData.Length);
byte[] compressed = Compress(fileData);
swapBytes(ref compressed, compressed.Length);
File.WriteAllBytes("_resources\\" + fileHeader.CRC.ToString("X"), compressed);
}
private void saveFolder()
{
File.Copy("resource.s4hd", "S4Resource.backup\\" + DateTime.Now.Year + "-" + DateTime.Now.Month + "-" + DateTime.Now.Day + "_" + +DateTime.Now.Hour + "-" + DateTime.Now.Minute + "-" + DateTime.Now.Second + "_" + "_resource.s4hd");
BinaryWriter bw = new BinaryWriter(new FileStream(fileName, FileMode.OpenOrCreate));
bw.Write((int)1);
List<FileHeader> fileHeaderList = fileHeaders.Values.ToList<FileHeader>();
bw.Write(fileHeaderList.Count);
foreach (FileHeader fileHeader in fileHeaderList)
{
byte[] header = new byte[272];
BinaryWriter bw2 = new BinaryWriter(new MemoryStream(header, true));
byte[] name = Encoding.ASCII.GetBytes(fileHeader.FileName);
if (name.Length > 256) continue;
byte[] name256 = new byte[256];
name.CopyTo(name256, 0);
bw2.Write(name256);
bw2.Write(fileHeader.CRC);
bw2.Write(fileHeader.FileSize);
bw2.Write(fileHeader.Unknown);
bw2.Close();
swapBytes(ref header, 272);
encryptCapped256(ref header, 272);
byte[] compressed = Compress(header);
swapBytes(ref compressed, compressed.Length);
encryptCapped256(ref compressed, compressed.Length);
bw.Write(compressed.Length);
bw.Write(compressed);
}
bw.Close();
}
private long GenerateCRC(string fullPath, ref byte[] fileData)
{
crc32 crc = new crc32();
long dataCRC = crc.CRC(fileData);
long nameCRC = crc.CRC(Encoding.ASCII.GetBytes(fullPath));
long finalCRC = dataCRC | (nameCRC << 32);
byte[] byteCRC = new byte[8];
BinaryWriter bw = new BinaryWriter(new MemoryStream(byteCRC, true));
bw.Write(finalCRC);
encryptCapped256(ref byteCRC, 8);
BinaryReader br = new BinaryReader(new MemoryStream(byteCRC));
return br.ReadInt64();
}
internal void exportFile(string fullPath, ref Stream file)
{
try
{
Directory.SetCurrentDirectory(workingPath);
FileHeader fileHeader = fileHeaders[fullPath];
if (fileHeader.FileName == null) return;
String path = "_resources\\" + fileHeader.CRC.ToString("X");
byte[] data = File.ReadAllBytes(path);
swapBytes(ref data, data.Length);
byte[] decompressed = Decompress(data,fileHeader.FileSize);
int size = fileHeader.FileSize;
decryptCapped256(ref decompressed, size);
if (fullPath.EndsWith(".x4"))
{
decompressed = decryptX4(decompressed, size, out size);
}
String test = Encoding.UTF8.GetString(decompressed);
file.Write(decompressed, 0, size);
file.Close();
}
catch (Exception e)
{
MessageBox.Show("Exception: "+e.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
private byte[] decryptX4(byte[] decompressed, int size, out int realSize)
{
BinaryReader br = new BinaryReader(new MemoryStream(decompressed));
realSize = br.ReadInt32();
byte[] data = br.ReadBytes(size - 4);
decrypt(ref data, data.Length);
return Decompress(data, realSize);
}
internal void clean()
{
if (!Directory.Exists("_resources") || Directory.Exists("_resources.bkp")) return;
Directory.SetCurrentDirectory(workingPath);
Directory.Move("_resources", "_resources.bkp");
Directory.CreateDirectory("_resources");
foreach (FileHeader fileHeader in fileHeaders.Values.ToList<FileHeader>())
{
if (File.Exists("_resources.bkp\\" + fileHeader.CRC.ToString("X")))
{
File.Move("_resources.bkp\\" + fileHeader.CRC.ToString("X"), "_resources\\" + fileHeader.CRC.ToString("X"));
}
}
MessageBox.Show("You can delete the folder _resources.bkp now.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
public byte[] Compress(byte[] src)
{
byte[] dst = new byte[src.Length + src.Length / 64 + 16 + 3 + 4];
int outlen = 0;
lzo1x_1_compress(src, src.Length, dst, ref outlen, _workMemory);
byte[] ret = new byte[outlen];
Array.Copy(dst, 0, ret, 0, outlen);
return ret;
}
public byte[] Decompress(byte[] src, int size)
{
int origlen = size;
byte[] dst = new byte[origlen];
int outlen = origlen;
lzo1x_decompress(src, src.Length - 4, dst, ref outlen, _workMemory);
return dst;
}
}
}
The GUI Software has been removed. DON'T ASK ME FOR IT! Thanks. Ask someone of the leechers, who already downloaded it. Or write it yourself.