Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes #37

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#ignore the following directories
.vs/
.*
!/.gitignore
!.gitattributes
Debug/
Release/
Debug (GUI)/
Expand Down
12 changes: 12 additions & 0 deletions DecimaExplorer.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,78 +62,90 @@
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (GUI)|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer-GUI</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (GUI-Repack)|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer-GUI-Repack</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (GUI)|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer-GUI</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (GUI-Repack)|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer-GUI-Repack</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (GUI)|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer-GUI</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (GUI-Repack)|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer-GUI-Repack</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (GUI)|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer-GUI</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (GUI-Repack)|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<TargetName>DecimaExplorer-GUI-Repack</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
Expand Down
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ Decima Explorer supports both repacking and packing. Please note there are some

## Usage

There are two flavours of Decima Explorer, one that can be run from the command line and one that runs as a Graphical User Interface. If the Ooz library fails to decompress a file you will need to use a version of the oodle dll. Repacking will require the oodle dll.
There are two flavours of Decima Explorer, one that can be run from the command line and one that runs as a Graphical User Interface. If the Ooz library fails to decompress a file you will need to use a version of the oodle dll (x64 as this is a 64-bit program). Repacking will require the oodle dll.

### GUI

With the GUI version you can select the initial data directory of the game and it will populate a file list based on the games cache prefetch. You can use the keyboard shortcut Ctrl+F to filter this list for the items you are interested in. You can select all the items with Ctrl+A or by Ctrl or shift clicking. With the items you wish to extract selected you can press the extract button and choose a directory in which to extract, when extracting multiple files with the GUI extraction will be multithreaded and should use all available cores. It is currently not possible to extract .mpk archives with the GUI.
With the GUI version you can select the initial data directory of the game and it will populate a file list based on the games cache prefetch (not complete!). You can use the keyboard shortcut Ctrl+F to filter this list for the items you are interested in. You can select all the items with Ctrl+A or by Ctrl or shift clicking. With the items you wish to extract selected you can press the extract button and choose a directory in which to extract, when extracting multiple files with the GUI extraction will be multithreaded and should use all available cores. It is currently not possible to extract .mpk archives with the GUI.

There is also a separate GUI for packing and repacking files. I decided to separate this for now for a cleaner UX. When repacking you must first select a folder that contains the complete path for a file, this is because the directory is used when hashing. You can then select and output, if it is a bin file that already exists it will attempt to repack that file. If it is a file that doesnt exist it will pack the files into a new bin file.

### CLI

With the CLI version there are various commands that can be used, they are list, extract, pack and repack. List will dump all the strings from the game's cache prefetch. Extract can extract either with a directory as the input or by file. When extracting by file you can use the file ID to extract as well, this is useful as it doesn't require knowing the filename to extract a particular entry. This currently supports both .bin and .mpk archives. Repack can be used to repack core files to their original .bin file. A root directory should be chosen so that the path from the root directory will match the hashed file name. For example if you extract a file and keep its original filename and directory structure to C:\Files, you can repack by using C:\Files as the base path. Pack uses a base directory the same way as Repack but instread of an existing Bin as the input, it allows you to specify an output bin file to create. Packing and Repacking require oo2core_7_win64.dll to be present alongside Decima Explorer. Below are example instructions that can be used on the command line;
With the CLI version there are various commands that can be used, they are list, extract, pack and repack. List will dump all the strings from the game's cache prefetch (not complete!). Extract can extract either with a directory as the input or by file. When extracting by file you can use the file ID to extract as well, this is useful as it doesn't require knowing the filename to extract a particular entry. This currently supports both .bin and .mpk archives. Repack can be used to repack core files to their original .bin file. A root directory should be chosen so that the path from the root directory will match the hashed file name. For example if you extract a file and keep its original filename and directory structure to C:\Files, you can repack by using C:\Files as the base path. Pack uses a base directory the same way as Repack but instread of an existing Bin as the input, it allows you to specify an output bin file to create. Packing and Repacking require oo2core_7_win64.dll to be present alongside Decima Explorer. Below are example instructions that can be used on the command line;

```
DecimaExplorer.exe -list "G:\path\to\game\data\files"
Expand All @@ -57,7 +57,12 @@ The same command can be used on movie files.
```
DecimaExplorer.exe -extract input.bin /file/name/to/extract output.bin
```
The example above is simlar to the last however the file's name is used to chose which file to extract. Only the extract and list commands are implemented for now.
The example above is similar to the last however the file's name is used to chose which file to extract. Only the extract and list commands are implemented for now.

```
DecimaExplorer.exe -extract input.bin *
```
Extracts all IDs in .bin, same as passing ID 0/1/2.../N manually (may print an error at the end, ignore).

```
DecimaExplorer.exe -extract "G:\path\to\game\data\files" /file/name/to/extract output.bin
Expand All @@ -69,6 +74,11 @@ DecimaExplorer.exe -extract "G:\path\to\game\data\files" /file/name/to/extract
```
It is possible to omit the output file, in this case the input filename or fileID will be used as the file name. If it is a directory, the directory structure will be created.

```
DecimaExplorer.exe -extract "G:\path\to\game\data\files" filelist.txt
```
Rather than extracting one by one, you can pass a text list with all names you want to extract. Since not all names are listed in the prefetch list (-l) this is useful to get extra files in bulk. Names can be found in various .core files from the prefetch list. Non-existant names are ignored.

```
DecimaExplorer.exe -pack "G:\path\to\files\to\pack" output.bin
```
Expand Down
4 changes: 2 additions & 2 deletions decima/archive/DecimaArchive.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ class DecimaArchive {
std::string getFilename();
std::string getExtension();
void setMessageHandler(MessageHandler* messageHandler);
virtual int extractFile(uint32_t id, std::string output) = 0;
virtual int extractFile(std::string filename, std::string output, bool suppressError = 0) = 0;
virtual int extractFile(uint32_t id, const char* output) = 0;
virtual int extractFile(std::string filename, const char* output, bool suppressError = 0) = 0;
};
65 changes: 53 additions & 12 deletions decima/archive/bin/ArchiveBin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ void ArchiveBin::parseFileTable(FILE* f, uint64_t fileTableCount) {
fread(&fileEntry.key2, 4, 1, f);

fileTable.push_back(fileEntry);
//hashFiles[fileEntry.hash] = &fileEntry;
}
}

Expand Down Expand Up @@ -248,9 +249,46 @@ uint32_t ArchiveBin::getFileEntryIndex(const std::string& filename) {
if (fileTable[i].hash == hash)
return i;
}

//todo
/*
bool found = hashFiles.find(hash) != hashFiles.end();
if (found) {
return hashFiles[hash]->entryNum;
}
*/

return -1;
}

//todo unify with interface
uint32_t ArchiveBin::getFileEntryIndexExt(std::string& filename) {
const int max_extensions = 6;
const char* extensions[max_extensions] = {"core", "stream", "core.stream", "coretext", "coredebug", "dep"};

// try default name first
std::string fname = filename;
uint32_t index = getFileEntryIndex(fname);
if (index != -1) {
return index;
}

// try common extensions (files like .soundbank.core exists in prefetch list so extension check isn't good)
for (int i = 0; i < max_extensions; i++) {
fname = filename;
addExtension(fname, extensions[i]);

index = getFileEntryIndex(fname);
if (index != -1) {
filename = fname;
return index;
}
}

return -1;
}


BinFileEntry ArchiveBin::getFileEntry(int id) {
for (int i = 0; i < fileTable.size(); i++) {
if (fileTable[i].entryNum == id)
Expand Down Expand Up @@ -399,6 +437,7 @@ DataBuffer ArchiveBin::createFileEntries(const std::string& basePath, const std:
fileEntry.offset = pos;
fileEntry.size = filesize;
fileTable.push_back(fileEntry);
//hashFiles[fileEntry.hash] = &fileEntry;
}

int writePos = isUpdate ? pos - header.dataSize : pos;
Expand Down Expand Up @@ -471,6 +510,7 @@ void ArchiveBin::swapEntries(const std::vector<Swapper>& swapMap) {
std::string firstFile = swapMap[i].firstFile;
std::string secondFile = swapMap[i].secondFile;

//todo fix extensions
if (!hasExtension(firstFile)) addExtension(firstFile, "core");
if (!hasExtension(secondFile)) addExtension(secondFile, "core");

Expand Down Expand Up @@ -543,40 +583,41 @@ int ArchiveBin::update(const std::string& basePath, const std::vector<std::strin
return 1;
}

int ArchiveBin::extractFile(uint32_t id, std::string output) {
if (!hasExtension(output)) addExtension(output, "core");

int ArchiveBin::extractFile(uint32_t id, const char* output) {
uint32_t i = getFileEntryIndex(id);

if (i == -1) {
this->messageHandler->showError(FILEINDEXERROR);
return 0;
}

//if (!hasExtension(output)) addExtension(output, "core");

DataBuffer data = extract(fileTable[i]);
if (!writeDataToFile(data, output)) return 0;
return 1;
}

int ArchiveBin::extractFile(std::string filename, std::string output, bool suppressError) {
if (!hasExtension(filename)) addExtension(filename, "core");
if (!hasExtension(output)) addExtension(output, "core");
uint32_t i = getFileEntryIndex(filename);

int ArchiveBin::extractFile(std::string filename, const char* output, bool suppressError) {
uint32_t i = getFileEntryIndexExt(filename);
if (i == -1) {
if (!suppressError) this->messageHandler->showError(FILENAMEERROR);
return 0;
}

std::string outname;
if (output == NULL)
outname = filename;
else
outname = output;

DataBuffer data = extract(fileTable[i]);
if (!writeDataToFile(data, output)) return 0;
if (!writeDataToFile(data, outname)) return 0;
return 1;
}

DataBuffer ArchiveBin::extractFile(std::string filename) {
DataBuffer data;
if (!hasExtension(filename)) addExtension(filename, "core");
uint32_t i = getFileEntryIndex(filename);
uint32_t i = getFileEntryIndexExt(filename);

if (i == -1) {
this->messageHandler->showError(FILENAMEERROR);
Expand Down
7 changes: 5 additions & 2 deletions decima/archive/bin/ArchiveBin.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "../DecimaArchive.h"
//#include <unordered_map>

typedef struct BinHeader {
uint32_t magic;
Expand Down Expand Up @@ -37,6 +38,7 @@ class ArchiveBin : public DecimaArchive {
BinHeader header = {0};
std::vector<BinFileEntry> fileTable;
std::vector<BinChunkEntry> chunkTable;
//std::unordered_map<uint64_t, BinFileEntry*> hashFiles;

uint32_t murmurSalt[4] = { 0x0FA3A9443, 0x0F41CAB62, 0x0F376811C, 0x0D2A89E3E };
uint32_t murmurSalt2[4] = { 0x06C084A37, 0x07E159D95, 0x03D5AF7E8, 0x018AA7D3F };
Expand Down Expand Up @@ -69,6 +71,7 @@ class ArchiveBin : public DecimaArchive {
DataBuffer getChunkData(BinChunkEntry chunkEntry);
void decryptChunkData(int32_t id, DataBuffer* data);
uint32_t getFileEntryIndex(const std::string& filename);
uint32_t getFileEntryIndexExt(std::string& filename);


uint64_t calculateDataOffset();
Expand Down Expand Up @@ -100,10 +103,10 @@ class ArchiveBin : public DecimaArchive {

int open() override;
DataBuffer extractFile(std::string filename);
int extractFile(uint32_t id, std::string output);
int extractFile(uint32_t id, const char* output);
void swapEntries(const std::vector<Swapper>& swapMap);
void nukeHashes(const std::vector<std::string>& fileList);
int extractFile(std::string filename, std::string output, bool suppressError = 0);
int extractFile(std::string filename, const char* output, bool suppressError = 0);
int create(const std::string& basePath, const std::vector<std::string>& fileList);
int update(const std::string& basePath, const std::vector<std::string>& fileList);
const std::vector<BinFileEntry>& getFileTable() { return fileTable; }
Expand Down
4 changes: 2 additions & 2 deletions decima/archive/mpk/ArchiveMoviePack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ uint32_t ArchiveMoviePack::getFileEntryIndex(std::string filename) {
return -1;
}

int ArchiveMoviePack::extractFile(uint32_t id, std::string output) {
int ArchiveMoviePack::extractFile(uint32_t id, const char* output) {
if (id < 0 || id >= fileTable.size()) {
this->messageHandler->showError(FILEINDEXERROR);
return 0;
Expand All @@ -45,7 +45,7 @@ int ArchiveMoviePack::extractFile(uint32_t id, std::string output) {
return 1;
}

int ArchiveMoviePack::extractFile(std::string filename, std::string output, bool suppressError) {
int ArchiveMoviePack::extractFile(std::string filename, const char* output, bool suppressError) {
uint32_t i = getFileEntryIndex(filename);

if (i == -1) {
Expand Down
4 changes: 2 additions & 2 deletions decima/archive/mpk/ArchiveMoviePack.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ class ArchiveMoviePack : public DecimaArchive {
ArchiveMoviePack(std::string filename);
~ArchiveMoviePack();
int open() override;
int extractFile(uint32_t id, std::string output);
int extractFile(std::string filename, std::string output, bool suppressError = 0);
int extractFile(uint32_t id, const char* output);
int extractFile(std::string filename, const char* output, bool suppressError = 0);
};
Loading