diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8bb671c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+.vs/
+Debug/
+Release/
+X64/
+mgs/3rdparty/zlib/zconf.h
+mgs/3rdparty/zlib/zlib.h
+mgs/3rdparty/zlib/zlib.lib
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..afa6f93
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+
+# Rex
+
+
+Rex is a free open source tool designed to be used with the game Metal Gear Solid on the original Playstation. It allows the user to extract Dir and Dar archive files packaged with the game
+
+### To Do
+ - Add multithreaded extract
+ - Add error handling
+ - Create GUI variant
+ - Clean up the code
+
+## Usage
+
+Currently only a CLI version of the application exists. The program is primitive and just takes the filename as the only required argument. An optional output directory can also be added. It is also possible to just drag the file you wish to extract on the executable.
+
+```
+Rex.exe "path\to\stage.dir"
+```
+The above instruction will extract all files from stage.dir to the current directory.
+
+```
+Rex.exe "path\to\res_mdl0.dar"
+```
+The same can be used for Qar, Dar or Slot files.
+
+```
+Rex.exe "path\to\stg_tex1.dar" "path\to\output"
+```
+An optional output path can be added at the end, if it is not included then it will extract to the directory of the file being extracted.
+
+## License
+[MIT](LICENSE.md)
+This project falls under the MIT license.
\ No newline at end of file
diff --git a/Rex.sln b/Rex.sln
new file mode 100644
index 0000000..07a81a5
--- /dev/null
+++ b/Rex.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30907.101
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Rex", "Rex.vcxproj", "{77E3F2BF-D6A0-4E75-AF00-32318AE9F776}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {77E3F2BF-D6A0-4E75-AF00-32318AE9F776}.Debug|x64.ActiveCfg = Debug|x64
+ {77E3F2BF-D6A0-4E75-AF00-32318AE9F776}.Debug|x64.Build.0 = Debug|x64
+ {77E3F2BF-D6A0-4E75-AF00-32318AE9F776}.Debug|x86.ActiveCfg = Debug|Win32
+ {77E3F2BF-D6A0-4E75-AF00-32318AE9F776}.Debug|x86.Build.0 = Debug|Win32
+ {77E3F2BF-D6A0-4E75-AF00-32318AE9F776}.Release|x64.ActiveCfg = Release|x64
+ {77E3F2BF-D6A0-4E75-AF00-32318AE9F776}.Release|x64.Build.0 = Release|x64
+ {77E3F2BF-D6A0-4E75-AF00-32318AE9F776}.Release|x86.ActiveCfg = Release|Win32
+ {77E3F2BF-D6A0-4E75-AF00-32318AE9F776}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {F4E5F536-F811-4C8E-A1AA-776CECF68C1E}
+ EndGlobalSection
+EndGlobal
diff --git a/Rex.vcxproj b/Rex.vcxproj
new file mode 100644
index 0000000..77b7ed6
--- /dev/null
+++ b/Rex.vcxproj
@@ -0,0 +1,167 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {77e3f2bf-d6a0-4e75-af00-32318ae9f776}
+ Rex
+ 10.0
+
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Rex.vcxproj.filters b/Rex.vcxproj.filters
new file mode 100644
index 0000000..8007791
--- /dev/null
+++ b/Rex.vcxproj.filters
@@ -0,0 +1,60 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Rex.vcxproj.user b/Rex.vcxproj.user
new file mode 100644
index 0000000..b9a8ff2
--- /dev/null
+++ b/Rex.vcxproj.user
@@ -0,0 +1,10 @@
+
+
+
+ true
+
+
+ STAGE.DIR
+ WindowsLocalDebugger
+
+
\ No newline at end of file
diff --git a/interface/cli.cpp b/interface/cli.cpp
new file mode 100644
index 0000000..979fcbf
--- /dev/null
+++ b/interface/cli.cpp
@@ -0,0 +1,89 @@
+#include "cli.h"
+
+CLI::CLI(int argc, char** argv) {
+ this->argc = argc;
+ this->argv = argv;
+}
+
+CLI::~CLI() {
+
+}
+
+bool isDar(std::string& filepath) {
+ return getExtension(filepath) == ".dar";
+}
+
+bool isStage(std::string& filepath) {
+ if (getExtension(filepath) == ".DIR" || getExtension(filepath) == ".dir") {
+ std::string filename = getExtensionlessName(filepath);
+ if (filenameContainsString(filepath, "STAGE") || filenameContainsString(filepath, "stage"))
+ return true;
+ }
+
+ return false;
+}
+
+void CLI::processCommands() {
+ while (currentArg < 2 && isCommand(argv[currentArg])) {
+ setCommand(argv[currentArg]);
+ currentArg++;
+ }
+}
+
+void CLI::setCommand(char* arg) {
+
+ printf("command not recognised\n");
+}
+
+void CLI::processArgs() {
+ processFile();
+}
+
+void CLI::processFile() {
+ std::string input = argv[currentArg];
+ std::string output = "";
+ currentArg++;
+
+
+ if (currentArg == argc - 1) output = argv[currentArg];
+
+ if (isDar(input)) {
+ Dar dar = Dar(input);
+ dar.extractAll(output);
+
+ exit(); return;
+ }
+
+ if (isStage(input)) {
+ Stage stage = Stage(input);
+ stage.open();
+ stage.extractAll(output);
+
+ exit(); return;
+ }
+
+}
+
+bool CLI::checkInput() {
+ if (argc > 1 && argc < 3) return true;
+ printUsage();
+ return false;
+}
+
+void CLI::run(std::string programName, std::string version) {
+ printf("Running %s v%s: Visit https://github.com/Jayveer/Rex for updates:\n", programName.c_str(), version.c_str());
+ if (!checkInput()) return;
+ processArgs();
+}
+
+bool CLI::isCommand(char* arg) {
+ return arg[0] == 0x2D;
+}
+
+void CLI::printUsage() {
+ printf(this->USAGE_MESSAGE);
+}
+
+void CLI::exit() {
+ printf(this->EXIT_MESSAGE);
+}
\ No newline at end of file
diff --git a/interface/cli.h b/interface/cli.h
new file mode 100644
index 0000000..1b15605
--- /dev/null
+++ b/interface/cli.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "../mgs/archive/dar/dar.h"
+#include "../mgs/archive/stage/stage.h"
+
+class CLI {
+public:
+ CLI(int argc, char** argv);
+ ~CLI();
+
+ void run(std::string programName, std::string version);
+ void exit();
+private:
+ int argc;
+ char** argv;
+ int currentArg = 1;
+
+ void printUsage();
+ bool checkInput();
+ void processArgs();
+ void processFile();
+ void processCommands();
+ bool isCommand(char* arg);
+ void setCommand(char* arg);
+
+ const char* EXIT_MESSAGE = "Exiting\n";
+ const char* USAGE_MESSAGE = "Usage:\t Rex.exe [OUTPUTDIRECTORY] \n";
+};
\ No newline at end of file
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..d2509e6
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,6 @@
+#include "interface/cli.h"
+
+int main(int argc, char** argv) {
+ CLI cli = CLI(argc, argv);
+ cli.run("Rex", "1.0");
+}
\ No newline at end of file
diff --git a/mgs/archive/dar/dar.cpp b/mgs/archive/dar/dar.cpp
new file mode 100644
index 0000000..2383e54
--- /dev/null
+++ b/mgs/archive/dar/dar.cpp
@@ -0,0 +1,50 @@
+#include "dar.h"
+
+Dar::Dar(std::string filename) {
+ std::ifstream fs;
+ this->dataSize = std::filesystem::file_size(filename);
+
+ fs.open(filename, std::ios::binary);
+ uint8_t* p = new uint8_t[dataSize];
+ fs.read((char*)p, dataSize);
+ this->darData = p;
+ fs.close();
+}
+
+Dar::~Dar() {
+ delete[] darData;
+}
+
+void Dar::extractAll(std::string& output) {
+ int ptr = 0;
+
+ updateDir("dar", output);
+
+ while (ptr < dataSize) {
+ DarEntry* entry = (DarEntry*)&darData[ptr];
+ std::string filename = intToHexString(entry->strcode) + "." + getExtForID(entry->extension);
+ writeDataToFile(&darData[ptr + 8], entry->size, filename, output);
+ ptr += (entry->size) + 8;
+ }
+
+ resetDir(output);
+}
+
+uint8_t* Dar::findFile(uint16_t id, uint16_t ext, int& size) {
+ int ptr = 0;
+
+ while (ptr < dataSize) {
+ DarEntry* entry = (DarEntry*)&darData[ptr];
+
+ if (entry->strcode == id && entry->extension == ext) {
+ size = entry->size;
+ uint8_t* file = new uint8_t[size];
+ memcpy(file, &darData[ptr + 8], size);
+ return file;
+ }
+
+ ptr += (entry->size) + 8;
+ }
+
+ return NULL;
+}
\ No newline at end of file
diff --git a/mgs/archive/dar/dar.h b/mgs/archive/dar/dar.h
new file mode 100644
index 0000000..e3bfbc0
--- /dev/null
+++ b/mgs/archive/dar/dar.h
@@ -0,0 +1,25 @@
+#pragma once
+#include
+#include
+#include "../../common/util.h"
+#include "../../common/fileutil.h"
+#include "../../common/ext_table.h"
+
+struct DarEntry {
+ uint16_t strcode;
+ uint16_t extension;
+ uint32_t size;
+ uint8_t data[];
+};
+
+class Dar {
+public:
+ Dar(std::string filename);
+ ~Dar();
+
+ void extractAll(std::string& output);
+ uint8_t* findFile(uint16_t id, uint16_t ext, int& size);
+private:
+ uint8_t* darData;
+ int dataSize;
+};
\ No newline at end of file
diff --git a/mgs/archive/stage/stage.cpp b/mgs/archive/stage/stage.cpp
new file mode 100644
index 0000000..82ce864
--- /dev/null
+++ b/mgs/archive/stage/stage.cpp
@@ -0,0 +1,101 @@
+#include "stage.h"
+
+Stage::Stage(std::string filename) {
+ this->filename = filename;
+}
+
+Stage::Stage(std::string filename, uint32_t sector) {
+ this->sector = sector;
+ this->filename = filename;
+}
+
+Stage::~Stage() {}
+
+int64_t Stage::getNextPageOffset(uint16_t pageID) {
+ int numStages = tableSize / 12;
+ return (pageID == numStages - 1) ? getFileSize(filename) : table[pageID + 1].offset * sector;
+}
+
+void Stage::open() {
+ std::ifstream stageDir;
+ stageDir.open(filename, std::ios::binary);
+ stageDir.read((char*)&this->tableSize, 4);
+
+ int numStages = tableSize / 12;
+
+ table.resize(numStages);
+ stageDir.read((char*)&this->table[0], tableSize);
+}
+
+void Stage::extractFiles(int64_t size, int64_t offset, std::string& output) {
+ std::ifstream fileDat;
+ fileDat.open(filename, std::ios::binary);
+ fileDat.seekg(offset);
+
+ uint8_t* pageData = new uint8_t[size];
+ fileDat.read((char*)pageData, size);
+ fileDat.close();
+
+ DataConfig cnf = DataConfig(pageData, size);
+ cnf.setHandler(this);
+ cnf.setWorkDir(output);
+ cnf.read(BINARY);
+
+ delete[] pageData;
+}
+
+int getStrLength(char* str) {
+ for (int i = 0; i < 8; i++) {
+ if (str[i] == '\0')
+ return i;
+ }
+
+ return 9;
+}
+
+void Stage::extract(uint16_t pageID, std::string output) {
+ int numStages = tableSize / 12;
+ if (pageID > numStages) return;
+
+ int strlen = getStrLength(table[pageID].name);
+
+ std::string stageName;
+ stageName.reserve(strlen);
+ stageName.assign(table[pageID].name, strlen);
+
+ if (strlen == 9) stageName[8] = '\0';
+
+ updateDir("stage", output);
+ updateDir(stageName, output);
+ int64_t nextOffset = getNextPageOffset(pageID);
+ int64_t offset = table[pageID].offset * sector;
+ int64_t size = nextOffset - offset;
+ extractFiles(size, offset, output);
+}
+
+void Stage::extractAll(std::string output) {
+ int numStages = tableSize / 12;
+
+ for (int i = 0; i < numStages; i++) {
+ extract(i, output);
+ }
+}
+
+////
+
+int32_t Stage::getSectorSize() {
+ return this->sector;
+};
+
+int64_t Stage::getNextSectorOffset(int64_t currentOffset) {
+ return getAlignment(currentOffset, sector);
+};
+
+void Stage::processQar(std::string filename, std::string region, std::string* workDir) {};
+void Stage::processAfp(std::string filename, std::string region, std::string* workDir) {};
+void Stage::processVram(std::string filename, std::string region, std::string* workDir) {};
+void Stage::processEnc(std::string region, uint8_t** section, int64_t size, int64_t sectionSize) {};
+
+void Stage::processFile(std::string filename, std::string region, std::string* workDir, uint8_t* fileData, int size) {
+ writeDataToFile(fileData, size, filename, *workDir);
+};
\ No newline at end of file
diff --git a/mgs/archive/stage/stage.h b/mgs/archive/stage/stage.h
new file mode 100644
index 0000000..00ed48e
--- /dev/null
+++ b/mgs/archive/stage/stage.h
@@ -0,0 +1,37 @@
+#pragma once
+#include "../../config/cnf/cnf.h"
+#include "../../common/fileutil.h"
+
+struct StageTable {
+ char name[8];
+ uint32_t offset;
+};
+
+class Stage : public CNFHandler {
+public:
+ Stage(std::string filename);
+ Stage(std::string filename, uint32_t sector);
+
+ ~Stage();
+
+ void open();
+ void extractAll(std::string output = "");
+ void extract(uint16_t pageID, std::string output = "");
+private:
+ int sector = 0x800;
+ std::string filename;
+
+ int32_t tableSize;
+ std::vector table;
+
+ int64_t getNextPageOffset(uint16_t pageID);
+ void extractFiles(int64_t size, int64_t offset, std::string& output);
+
+ int32_t getSectorSize();
+ int64_t getNextSectorOffset(int64_t currentOffset);
+ void processQar(std::string filename, std::string region, std::string* workDir);
+ void processAfp(std::string filename, std::string region, std::string* workDir);
+ void processVram(std::string filename, std::string region, std::string* workDir);
+ void processEnc(std::string region, uint8_t** section, int64_t size, int64_t sectionSize);
+ void processFile(std::string filename, std::string region, std::string* workDir, uint8_t* fileData, int size);
+};
\ No newline at end of file
diff --git a/mgs/common/ext_table.h b/mgs/common/ext_table.h
new file mode 100644
index 0000000..5111b84
--- /dev/null
+++ b/mgs/common/ext_table.h
@@ -0,0 +1,43 @@
+#pragma once
+
+struct EXT_TABLE {
+ const char* name;
+ uint8_t id;
+};
+
+const EXT_TABLE ext_table[16] = {
+ {"bin", 0x62},
+ {"con", 0x63},
+ {"dar", 0x64},
+ {"efx", 0x65},
+ {"gcx", 0x67},
+ {"hzm", 0x68},
+ {"img", 0x69},
+ {"kmd", 0x6B},
+ {"lit", 0x6C},
+ {"mdx", 0x6D},
+ {"oar", 0x6F},
+ {"pcx", 0x70},
+ {"rar", 0x72},
+ {"sgt", 0x73},
+ {"wvx", 0x77},
+ {"zmd", 0x7A},
+};
+
+inline
+const char* getExtForID(uint8_t id) {
+ for (int i = 0; i < 16; i++) {
+ if (ext_table[i].id == id)
+ return ext_table[i].name;
+ }
+ return "";
+}
+
+inline
+uint8_t getIDforExt(const char* ext) {
+ for (int i = 0; i < 16; i++) {
+ if (!strcmp(ext_table[i].name, ext))
+ return ext_table[i].id;
+ }
+ return -1;
+}
\ No newline at end of file
diff --git a/mgs/common/fileutil.h b/mgs/common/fileutil.h
new file mode 100644
index 0000000..7dbaa8b
--- /dev/null
+++ b/mgs/common/fileutil.h
@@ -0,0 +1,81 @@
+#pragma once
+#include
+#include
+
+inline
+void updateDir(const std::string& path, std::string& output) {
+ std::filesystem::path p{ output };
+ p.append(path);
+ output = p.u8string();
+}
+
+inline
+void resetDir(std::string& output) {
+ std::filesystem::path p{ output };
+ output = p.parent_path().u8string();
+}
+
+inline
+std::string getCurrentDir(const std::string& output) {
+ std::filesystem::path p{ output };
+ return p.filename().u8string();
+}
+
+inline
+std::string getExtension(const std::string& output) {
+ std::filesystem::path p{ output };
+ return p.extension().u8string();
+}
+
+inline
+std::string getExtensionlessName(const std::string& output) {
+ std::filesystem::path p{ output };
+ return p.stem().u8string();
+}
+
+inline
+bool filenameContainsString(const std::string& output, const std::string& string) {
+ std::string filename = getExtensionlessName(output);
+
+ if (filename.find(string) != std::string::npos)
+ return true;
+
+ return false;
+}
+
+inline
+bool isDirectory(const std::string& output) {
+ std::filesystem::path p{ output };
+ return std::filesystem::is_directory(p);
+}
+
+inline
+bool fileExists(const std::string& output) {
+ std::filesystem::path p{ output };
+ return std::filesystem::exists(p);
+}
+
+inline
+int64_t getFileSize(const std::string& input) {
+ return std::filesystem::file_size(input);
+}
+
+inline
+int64_t getAlignment(int64_t currentOffset, int64_t alignSize) {
+ uint64_t step = (alignSize - (currentOffset % alignSize));
+ if (step != alignSize)
+ return step;
+ return 0;
+}
+
+inline
+void writeDataToFile(uint8_t* data, int size, const std::string& filename, std::string& output) {
+ if (!std::filesystem::exists(output))
+ std::filesystem::create_directories(output);
+
+ updateDir(filename, output);
+ std::ofstream ofs(output, std::ofstream::binary);
+ ofs.write((char*)data, size);
+ ofs.close();
+ resetDir(output);
+}
\ No newline at end of file
diff --git a/mgs/common/util.h b/mgs/common/util.h
new file mode 100644
index 0000000..b1c0c39
--- /dev/null
+++ b/mgs/common/util.h
@@ -0,0 +1,23 @@
+#pragma once
+#include
+
+inline
+std::string formatStrcode(std::string strcode) {
+ uint8_t length = strcode.size();
+ uint8_t pad = 6 - length;
+ std::string prefix;
+
+ for (int i = 0; i < pad; i++) {
+ prefix += "0";
+ }
+
+ return (prefix + strcode);
+
+}
+
+inline
+std::string intToHexString(int value) {
+ std::stringstream ss;
+ ss << std::hex << value;
+ return formatStrcode(ss.str());
+}
\ No newline at end of file
diff --git a/mgs/config/cnf/cnf.cpp b/mgs/config/cnf/cnf.cpp
new file mode 100644
index 0000000..80f765c
--- /dev/null
+++ b/mgs/config/cnf/cnf.cpp
@@ -0,0 +1,148 @@
+#include "cnf.h"
+
+DataConfig::DataConfig(std::string filename) {
+ this->filename = filename;
+}
+
+DataConfig::DataConfig(uint8_t* data, int size) {
+ this->data = data;
+ this->dataSize = size;
+}
+
+DataConfig::~DataConfig() {
+}
+
+void DataConfig::setWorkDir(std::string& workdir) {
+ this->workdir = &workdir;
+}
+
+void DataConfig::setHandler(CNFHandler* cnfHandler) {
+ this->cnfHandler = cnfHandler;
+}
+
+const char* DataConfig::getRegionForID(uint32_t id) {
+ for (int i = 0; i < 7; i++) {
+ if (cnfRegion[i].id == id)
+ return cnfRegion[i].name;
+ }
+ return "";
+}
+
+void DataConfig::readFlag(std::string line) {
+ std::stringstream lineStream(line);
+ std::vector tokens = { std::istream_iterator{lineStream}, std::istream_iterator{} };
+ std::string flagName = tokens[0];
+
+ currentRegion = flagName;
+}
+
+std::string DataConfig::getDarName() {
+ return currentRegion == "resident" ? "res_mdl" + std::to_string(resDarCount) : "stg_tex" + std::to_string(cacDarCount);
+}
+
+void DataConfig::readFile(DataCNFTag tag, uint8_t* data, int size) {
+ std::string outputName = tag.id ? intToHexString(tag.id) : getDarName();
+ outputName = outputName + "." + getExtForID(tag.extension);
+
+ if (cnfHandler) cnfHandler->processFile(outputName, currentRegion, workdir, data, size);
+}
+
+void DataConfig::startSection() {
+ section = &data[dataPtr];
+}
+
+void DataConfig::endSection(DataCNFTag tag) {
+ dataPtr += tag.offset;
+ dataPtr += cnfHandler->getNextSectorOffset(dataPtr);
+}
+
+void DataConfig::readCache(DataCNFTag tag, DataCNFTag nextTag) {
+ if (!tag.offset) startSection();
+
+ int size = nextTag.offset - tag.offset;
+
+ switch (tag.extension) {
+ case 0x62:
+ readFile(tag, §ion[tag.offset], size);
+ break;
+ case 0x64:
+ cacDarCount++;
+ readFile(tag, §ion[tag.offset], size);
+ break;
+ case 0xFF:
+ endSection(tag);
+ break;
+ default:
+ readFile(tag, §ion[tag.offset], size);
+ }
+}
+
+void DataConfig::readRegion(DataCNFTag tag) {
+ switch (tag.extension) {
+ case 0x62:
+ if (currentRegion == "sound") currentRegion = "stage";
+ readFile(tag, &data[dataPtr], tag.offset);
+ break;
+ case 0x64:
+ if (currentRegion != "resident") cacDarCount++;
+ readFile(tag, &data[dataPtr], tag.offset);
+ if (currentRegion == "resident") resDarCount++;
+ break;
+ default:
+ readFile(tag, &data[dataPtr], tag.offset);
+ }
+
+ endSection(tag);
+}
+
+void DataConfig::readLine(DataCNFTag tag, DataCNFTag nextTag) {
+ currentRegion = getRegionForID(tag.region);
+ currentRegion == "cache" ? readCache(tag, nextTag) : readRegion(tag);
+}
+
+void DataConfig::readLine(std::string line) {
+ switch (line.at(0)) {
+ case 0x2E:
+ readFlag(&line[1]);
+ break;
+ case 0x3F:
+ if (cnfHandler) cnfHandler->processAfp(&line[1], currentRegion, workdir);
+ break;
+ case 0x40:
+ if (cnfHandler) cnfHandler->processQar(&line[1], currentRegion, workdir);
+ break;
+ default:
+ if (cnfHandler) cnfHandler->processFile(line, currentRegion, workdir, NULL, 0);
+ }
+}
+
+void DataConfig::initBinaryStream() {
+ cnf = (DataCNF*)data;
+ dataPtr += cnf->cnfSize * cnfHandler->getSectorSize();
+}
+
+void DataConfig::initTextStream() {
+ std::string cnfTxt = std::string((char*)data, dataSize);
+ textStream.str(cnfTxt);
+}
+
+void DataConfig::readBinary() {
+ initBinaryStream();
+
+ for (int i = 0; cnf->tags[i].region != 0; i++) {
+ readLine(cnf->tags[i], cnf->tags[i + 1]);
+ }
+}
+
+void DataConfig::readASCII() {
+ initTextStream();
+
+ std::string line;
+ while (std::getline(textStream, line)) {
+ readLine(line);
+ }
+}
+
+void DataConfig::read(CNFTYPE type) {
+ type == BINARY ? readBinary() : readASCII();
+}
\ No newline at end of file
diff --git a/mgs/config/cnf/cnf.h b/mgs/config/cnf/cnf.h
new file mode 100644
index 0000000..bc9a6d1
--- /dev/null
+++ b/mgs/config/cnf/cnf.h
@@ -0,0 +1,90 @@
+#pragma once
+#include "../../common/util.h"
+#include "../../common/ext_table.h"
+
+#include
+
+struct DataCNFTag {
+ uint16_t id;
+ uint8_t region;
+ uint8_t extension;
+ uint32_t offset;
+};
+
+struct DataCNF {
+ uint16_t cnfSize;
+ uint16_t dataSize;
+ DataCNFTag tags[];
+};
+
+struct CNFREGION {
+ const char* name;
+ int id;
+};
+
+enum CNFTYPE {
+ ASCII,
+ BINARY
+};
+
+class CNFHandler {
+public:
+ virtual int32_t getSectorSize() = 0;
+ virtual int64_t getNextSectorOffset(int64_t currentOffset) = 0;
+ virtual void processQar(std::string filename, std::string region, std::string* workDir) = 0;
+ virtual void processAfp(std::string filename, std::string region, std::string* workDir) = 0;
+ virtual void processVram(std::string filename, std::string region, std::string* workDir) = 0;
+ virtual void processEnc(std::string region, uint8_t** section, int64_t size, int64_t sectionSize) = 0;
+ virtual void processFile(std::string filename, std::string region, std::string* workDir, uint8_t* fileData, int size) = 0;
+};
+
+class DataConfig {
+public:
+ DataConfig(std::string filename);
+ DataConfig(uint8_t* data, int size);
+
+ ~DataConfig();
+ void read(CNFTYPE type = ASCII);
+ void setWorkDir(std::string& workdir);
+ void setHandler(CNFHandler* cnfHandler);
+ const char* getRegionForID(uint32_t id);
+private:
+ int dataSize = 0;
+ DataCNF* cnf = {};
+ uint8_t* data = {};
+ int resDarCount = 0;
+ int cacDarCount = 0;
+ std::string filename;
+ uint8_t* section = {};
+ uint64_t dataPtr = 0;
+ std::string* workdir = {};
+ CNFHandler* cnfHandler = {};
+ std::string currentRegion;
+ std::stringstream textStream;
+
+ void readASCII();
+ void readBinary();
+ void initTextStream();
+ void initBinaryStream();
+ void readLine(std::string line);
+ void readFlag(std::string line);
+ void readLine(DataCNFTag tag, DataCNFTag nextTag);
+
+ std::string getDarName();
+ void readRegion(DataCNFTag tag);
+ void readCache(DataCNFTag tag, DataCNFTag nextTag);
+ void readDar(DataCNFTag tag, uint8_t* data, int size);
+ void readBin(DataCNFTag tag, uint8_t* data, int size);
+ void readFile(DataCNFTag tag, uint8_t* data, int size);
+
+ void startSection();
+ void endSection(DataCNFTag tag);
+
+ const CNFREGION cnfRegion[7] = {
+ { "end", 0x00 },
+ { "nocache", 0x6E },
+ { "cache", 0x63 },
+ { "resident", 0x72 },
+ { "sound", 0x73 }
+ };
+};
\ No newline at end of file