Skip to content

Commit

Permalink
Added WIP decryption support
Browse files Browse the repository at this point in the history
  • Loading branch information
Jayveer committed Jul 19, 2020
1 parent 992bb6a commit 80cd3dd
Show file tree
Hide file tree
Showing 10 changed files with 975 additions and 23 deletions.
16 changes: 11 additions & 5 deletions DecimaExplorer.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,32 @@
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{05B0E20F-1ED1-4EA1-B3BF-9FDFDD7E26B3}</ProjectGuid>
<RootNamespace>DecimaExplorer</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
Expand Down Expand Up @@ -117,18 +117,24 @@
<ItemGroup>
<ClCompile Include="archive\DecimaArchive.cpp" />
<ClCompile Include="cli\CLI.cpp" />
<ClCompile Include="hash\md5.c" />
<ClCompile Include="hash\MurmurHash3.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="ooz\Kraken.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="archive\DecimaArchive.h" />
<ClInclude Include="archive\DecimaArchiveError.h" />
<ClInclude Include="cli\CLI.h" />
<ClInclude Include="hash\md5.h" />
<ClInclude Include="hash\MurmurHash3.h" />
<ClInclude Include="ooz\Kraken.h" />
<ClInclude Include="ooz\targetver.h" />
</ItemGroup>
<ItemGroup>
<None Include="LICENSE.md" />
<None Include="ooz\LICENSE.md" />
<None Include="README.md" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
Expand Down
14 changes: 14 additions & 0 deletions DecimaExplorer.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
<ClCompile Include="cli\CLI.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hash\MurmurHash3.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hash\md5.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ooz\Kraken.h">
Expand All @@ -44,8 +50,16 @@
<ClInclude Include="cli\CLI.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hash\MurmurHash3.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hash\md5.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="ooz\LICENSE.md" />
<None Include="LICENSE.md" />
<None Include="README.md" />
</ItemGroup>
</Project>
4 changes: 4 additions & 0 deletions DecimaExplorer.vcxproj.user
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@
<LocalDebuggerCommandArguments>-e Initial.bin 0 c:\test.bin</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerCommandArguments>-e 7017f9bb9d52fc1c4433599203cc51b1.bin 0 0out.bin</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@
# Decima Explorer


Decima Explorer is a free and open-source program designed to allow you to unpack files from the archive structure used by games using the Decima engine. Currently files must be extracted using their ID as I am unaware of how the file names are mapped. I suspect a hashing algorithm is used for lookup but am unsure of which one.
Decima Explorer is a free and open-source program designed to allow you to unpack files from the archive structure used by games using the Decima engine. Currently files must be extracted using their ID as I am unaware of how the file names are mapped.

Early work-in-progress support for encrypted files has been added thanks to Ekey, and [Wunkolo](https://github.com/Wunkolo) for researching, implementing and documenting the decryption algorithm. Wunkolo's [implementation can be found here](https://github.com/Wunkolo).

This program uses [Ooz](https://github.com/powzix/ooz) created by [Powzix](https://github.com/powzix) for decompression. It is slightly modified for this programs purposes. Ooz is under GPL licensing.

This program also uses [Murmur3](https://github.com/PeterScott/murmur3) by [Peter Scott](https://github.com/PeterScott/murmur3).

If anyone has any issue with this project's existence please feel free to reach out to me.

### To Do
- Create GUI variant
- Implement Repack command
- Understand how the file strings match to files
- Understand how the file strings match to files (Appears to be Murmur3Hash but not seeing expected results)

## Usage

Expand Down
87 changes: 77 additions & 10 deletions archive/DecimaArchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
#include "DecimaArchiveError.h"

bool DecimaArchive::checkMagic() {
return header.magic == 0x20304050;
return (header.magic == 0x20304050) || (header.magic == 0x21304050);
}

bool DecimaArchive::isEncrypted() {
return header.magic == 0x21304050;
}

std::string DecimaArchive::getFilename() {
Expand All @@ -18,7 +22,7 @@ DecimaArchive::DecimaArchive(std::string filename) {
}

int DecimaArchive::getVersion() {
return header.version;
return header.key;
}

DecimaArchive::~DecimaArchive() {
Expand All @@ -38,7 +42,7 @@ int DecimaArchive::calculateChunkTableOffset(uint64_t fileTableCount) {

void DecimaArchive::parseHeader(FILE* f) {
fread(&header.magic, 4, 1, f);
fread(&header.version, 4, 1, f);
fread(&header.key, 4, 1, f);
fread(&header.fileSize, 8, 1, f);
fread(&header.dataSize, 8, 1, f);
fread(&header.fileTableCount, 8, 1, f);
Expand All @@ -51,11 +55,11 @@ void DecimaArchive::parseFileTable(FILE* f, uint64_t fileTableCount) {
DecimaFileEntry fileEntry;

fread(&fileEntry.entryNum, 4, 1, f);
fread(&fileEntry.unknown, 4, 1, f);
fread(&fileEntry.unknown2, 8, 1, f);
fread(&fileEntry.key, 4, 1, f);
fread(&fileEntry.hash, 8, 1, f);
fread(&fileEntry.offset, 8, 1, f);
fread(&fileEntry.size, 4, 1, f);
fread(&fileEntry.unknown3, 4, 1, f);
fread(&fileEntry.key2, 4, 1, f);

fileTable.push_back(fileEntry);
}
Expand All @@ -68,10 +72,10 @@ void DecimaArchive::parseChunkTable(FILE* f, uint64_t chunkTableCount) {

fread(&chunkEntry.uncompressedOffset, 8, 1, f);
fread(&chunkEntry.uncompressedSize, 4, 1, f);
fread(&chunkEntry.unknown, 4, 1, f);
fread(&chunkEntry.key, 4, 1, f);
fread(&chunkEntry.compressedOffset, 8, 1, f);
fread(&chunkEntry.compressedSize, 4, 1, f);
fread(&chunkEntry.unknown2, 4, 1, f);
fread(&chunkEntry.key2, 4, 1, f);

chunkTable.push_back(chunkEntry);
}
Expand Down Expand Up @@ -146,7 +150,9 @@ DataBuffer DecimaArchive::extract(DecimaFileEntry fileEntry) {
DataBuffer tempBuffer(maxNeededSize);

for (int i = firstChunkRow; i <= lastChunkRow; i++) {
decompressChunkData(getChunkData(chunkTable[i]), chunkTable[i].uncompressedSize, &tempBuffer[pos]);
DataBuffer chunkData = getChunkData(chunkTable[i]);
if (isEncrypted()) decryptChunkData(i, &chunkData);
decompressChunkData(chunkData, chunkTable[i].uncompressedSize, &tempBuffer[pos]);
pos += chunkTable[i].uncompressedSize;
}

Expand All @@ -160,6 +166,61 @@ DataBuffer DecimaArchive::extract(DecimaFileEntry fileEntry) {

}

void DecimaArchive::decryptHeader() {
uint32_t* p = (uint32_t*)&header + 2;
cipher(header.key, header.key + 1, p);
}

void DecimaArchive::decryptFileTable() {
for (int i = 0; i< header.fileTableCount; i++) {
uint32_t* p = (uint32_t*)&fileTable[i];
cipher(fileTable[i].key, fileTable[i].key2, p);
}
}

void DecimaArchive::decryptChunkTable() {
for (int i = 0; i < header.chunkTableCount; i++) {
uint32_t* p = (uint32_t*)&chunkTable[i];
uint32_t saveKey = chunkTable[i].key;
cipher(chunkTable[i].key, chunkTable[i].key2, p);
chunkTable[i].key = saveKey;
}
}

void DecimaArchive::decryptChunkData(int32_t id, DataBuffer* data) {
dataCipher(id, &(*data)[0], data->size());
}

void DecimaArchive::cipher(uint32_t key, uint32_t key2, uint32_t *src) {
uint32_t iv[4];
uint32_t inputKey[2][4] = {
{ key, murmurSalt[1], murmurSalt[2], murmurSalt[3] },
{ key2, murmurSalt[1], murmurSalt[2], murmurSalt[3] }
};

for (int i = 0; i < 2; i++) {
MurmurHash3_x64_128(inputKey[i], 0x10, seed, iv);
for (int j = 0; j < 4; j++) {
src[(i * 4) + j] ^= iv[j];
}
}
}

void DecimaArchive::dataCipher(uint32_t chunkID, uint8_t* src, int size) {
uint32_t iv[4];
MurmurHash3_x64_128(&chunkTable[chunkID].uncompressedOffset, 0x10, seed, iv);

for (int i = 0; i < 4; i++) {
iv[i] ^= murmurSalt2[i];
}

md5_byte_t* digest = md5Hash((md5_byte_t*)iv, 16);

for (int i = 0; i < size; i++) {
src[i] ^= digest[i % 16];
}
}

int DecimaArchive::open() {
FILE *f;
fopen_s(&f, getFilename().c_str(), "rb");
Expand All @@ -176,8 +237,14 @@ int DecimaArchive::open() {
return 0;
}

if (isEncrypted()) decryptHeader();

parseFileTable(f, header.fileTableCount);
if (isEncrypted()) decryptFileTable();

parseChunkTable(f, header.chunkTableCount);
if (isEncrypted()) decryptChunkTable();

fclose(f);
return 1;
}
Expand All @@ -193,4 +260,4 @@ int DecimaArchive::extractFile(uint32_t id, std::string output) {
DataBuffer data = extract(fileTable[i]);
if (!writeDataToFile(data, output)) return 0;
return 1;
}
}
26 changes: 20 additions & 6 deletions archive/DecimaArchive.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#pragma once

#include "../ooz/Kraken.h"
#include "../hash/md5.h"
#include "../hash/MurmurHash3.h"
#include <string>
#include <inttypes.h>
#include <vector>

typedef struct DecimaHeader {
uint32_t magic; //0x20304050
uint32_t version; //0x3D
uint32_t key;
uint64_t fileSize;
uint64_t dataSize;
uint64_t fileTableCount;
Expand All @@ -16,20 +19,20 @@ typedef struct DecimaHeader {

typedef struct DecimaFileEntry {
uint32_t entryNum;
uint32_t unknown;
uint64_t unknown2; //maybe file hash, not sure
uint32_t key;
uint64_t hash;
uint64_t offset;
uint32_t size;
uint32_t unknown3;
uint32_t key2;
} DecimaFileTable;

typedef struct DecimaChunkEntry {
uint64_t uncompressedOffset; //relative offset once uncompressed
uint32_t uncompressedSize;
uint32_t unknown;
uint32_t key;
uint64_t compressedOffset;
uint32_t compressedSize;
uint32_t unknown2;
uint32_t key2;
} DecimaChunkTable;

typedef std::vector<uint8_t> DataBuffer;
Expand All @@ -42,18 +45,29 @@ class DecimaArchive {

std::string filename;

uint8_t seed = 0x2A;
uint32_t murmurSalt[4] = { 0x0FA3A9443, 0x0F41CAB62, 0x0F376811C, 0x0D2A89E3E };
uint32_t murmurSalt2[4] = { 0x06C084A37, 0x07E159D95, 0x03D5AF7E8, 0x018AA7D3F };

void parseHeader(FILE* file);
void parseFileTable(FILE* f, uint64_t fileTableCount);
void parseChunkTable(FILE* f, uint64_t chunkTableCount);

bool checkMagic();
bool isEncrypted();
void decryptHeader();
void decryptFileTable();
void decryptChunkTable();
uint32_t getFileEntryIndex(int id);
DecimaFileEntry getFileEntry(int id);
void setFilename(std::string filename);
int findChunkWithOffset(uint64_t offset);
DataBuffer extract(DecimaFileEntry fileEntry);
void decryptChunkData(int32_t id, DataBuffer* data);
DataBuffer getChunkData(DecimaChunkEntry chunkEntry);
int calculateChunkTableOffset(uint64_t fileTableCount);
void cipher(uint32_t key, uint32_t key2, uint32_t* src);
void dataCipher(uint32_t chunkID, uint8_t* src, int size);
int writeDataToFile(DataBuffer data, std::string filename);
uint64_t calculateFirstContainingChunk(uint64_t fileOffset, int chunkSize);
uint64_t calculateLastContainingChunk(uint64_t fileOffset, int fileSize, int chunkSize);
Expand Down
Loading

0 comments on commit 80cd3dd

Please sign in to comment.