Tip: Try to check the Size and the CRC32 of the Entries:
IF STOREDSIZE=0 AND CRC32=0 ---> good entry, file with zero size
IF STOREDSIZE=0 AND CRC32<>0 ---> BAD ENTRY
Example bad entries from DHT:
(storedsize#flag#originalsize#crc32#unknown#filename)
Data.Shaders.Win32.v1.spk:
00000000#08#00000000#ED030B6F#16CA0B40#fxdata\_def_glossiness.png
00000000#08#00000000#81B267A3#16CA0B40#fxdata\_def_heatgradient.png
00000000#08#00000000#618B2EA4#16CA0B40#fxdata\_def_noise.png
Data.Shaders.Win32.v2.spk:
00000000#08#00000000#ED030B6F#16B697E0#fxdata\_def_glossiness.png
00000000#08#00000000#81B267A3#16C1DC60#fxdata\_def_heatgradient.png
00000000#08#00000000#618B2EA4#16B697E0#fxdata\_def_noise.png
O.K. Here's every important information I collected about the .SPK files:
There are 3 different versions of SPK files, so first always check the Header's version number! (
They use different Header size, and different Entry sizes!!!)
If 1 or 2, then no problem, but the 3 contains some more trick, the decoding needs a MASTER_KEY, which is game-dependant. These MASTER_KEYS are derived from some passwords:
Deer Hunter Tournament: (I didn't find demo version of the game, so this is for the full version)
MASTER_KEY=0x48C20726 comes from this: "winmm.lib!ddraw.lib"
Deer Hunter 2005 FULL:
MASTER_KEY=0x32E105A4 comes from this: "dh2005.exe;d3d.dll"
Deer Hunter 2005 DEMO:
MASTER_KEY=0x3CB80690 comes from this: "d3dx.lib;ddraw.dll"
DeerHunter 2005 custom level:
MASTER_KEY=0x18D9042F comes from this: "dh2005custom"
Trophy Hunter 2003 + Deer Hunter 2004 use the v1 or v2 format, so they don't need password or MASTER_KEY
Code: Select all
The .SPK file starts with this header:
Type DeerHuntTour_SPK_HeaderType = Packed Record // 12 or 13 bytes!
APK_ID : Array[0..2] Of char; // 'APK'
VersionByte : byte; // (1,2 or 3)
DirStartPos : dword; // points to the end of the file
DirOrigSize : dword; // the original, unpacked size of the directory data
// ----------------------------
// This, last byte is only if the VersionByte = 3 !!!!!
MoreCryptFlag : boolean; // 1 byte! Indicates more encryption!
End;
The file information data is compressed (with zlib) and encrypted.
So first we need do decrypt:
If version=1 or 2 then:
DeerHunter_Encrypt(Buffer,Hdr.DirStartPos * Hdr.DirOrigSize,FALSE);
If version=3 then:
you can generate the MASTER_KEY, from the appropriate password:
MASTER_KEY := DeerHunter_GenerateMasterKey('... place of the password...');
and then:
DeerHunter_Encrypt(Buffer,MASTER_KEY Xor Hdr.DirStartPos Xor Hdr.DirOrigSize,Hdr.MoreCryptFlagByte);
If everything is OK, the decoded buffer now contains zlib compressed data.
Unpack it. The unpacked data size must be equal to the value of Hdr.DirOrigSize
Now we can process the Entries (repeat until the end of the buffer):
Type DeerHuntTour_SPK_EntryType = Packed Record
Name : Array Of char; // The filename, with a terminating zero character
StartPos : dword; // start of the file data
StoredSize : dword; // the stored size (the compressed size, if compression used, otherwise the original size)
FlagsByte : byte; // 00 = stored
// 01 = encrypted
// 02 = zlib_compressed
// 03 = zlib_compressed+encrypted
OriginalSize : dword; // the original size
CRC32 : dword; // the crc32 value of the original file
//-----------------------------
// This, last dword is only if the VersionByte = 2 OR 3 !!!!!
unknown : dword; // absolutely unknown value
End;
If the file data is encypted ( (Entry.FlagsByte AND 1) = 1) then use the same decryption method as above:
If version=1 or 2 then:
DeerHunter_Encrypt(DataBuffer,(Entry.OriginalSize * Entry.StartPos) Xor Entry.CRC32 );
If version=3 then:
// we generated the MASTER_KEY, so we can use it again:
DeerHunter_Encrypt(DataBuffer,MASTERKEY Xor Entry.OriginalSize Xor Entry.StartPos Xor Entry.CRC32,Header.MoreCryptFlagByte);
Function DeerHunter_GenerateMasterKey(MasterKeyPhrase : PChar) : dword;
// source: Deniz Özmen http://oezmen.eu/gameresources/
Var I, Temp : dword;
MasterKey : dword;
Begin {Function DeerHunter_GenerateMasterKey}
MasterKey := 1;
For I := 0 To StrLen(MasterKeyPhrase) - 1 Do
Begin
Temp := (MasterKey And $0000ffff + Ord(MasterKeyPhrase[I])) Mod $0000fff1;
Asm
push ebx
mov eax, MasterKey
sar eax, 16
mov ebx, Temp
add eax, ebx
mov ecx, $0000fff1
cdq
idiv ecx
shl edx, 16
add edx, ebx
mov MasterKey, edx
pop ebx
End;
End;
Result := MasterKey;
End; {Function DeerHunter_GenerateMasterKey}
Procedure DeerHunter_Encrypt(Var Buffer : TArrayOfByte;
Key : dword;
AdditionalKeyRound : Boolean);
// source: Deniz Özmen http://oezmen.eu/gameresources/
Var I: Integer;
Begin {Procedure DeerHunter_Encrypt}
If AdditionalKeyRound Then // THIS CAN BE ONLY IF THE VERSION=3, OTHERWISE WE SKIP THIS!
Asm
mov eax, Key
mov ecx, Key
and eax, $0000ffff
mov edx, $00006054
mul edx
sar ecx, 16
add eax, ecx
mov Key, eax
End;
For I := 0 to High(Buffer) do // = Length(Buffer) - 1 !!!
Begin
Asm
mov eax, Key
mov ecx, Key
and eax, $0000ffff
mov edx, $00006054
mul edx
sar ecx, 16
add eax, ecx
mov Key, eax
End;
Buffer[I] := Buffer[I] Xor Key;
End;
End; {Procedure DeerHunter_Encrypt}