Removed unecessary files. Added zip_file lib to dkr_assets_tool

This commit is contained in:
David Benepe
2022-03-25 18:36:17 -04:00
parent 3e210904fe
commit 2e2ef0f1f5
21 changed files with 5709 additions and 5135 deletions
File diff suppressed because it is too large Load Diff
@@ -1,84 +0,0 @@
#include "DKRCompression.h"
DKRCompression::DKRCompression()
{
compressed.SetGame(DKR);
}
DKRCompression::~DKRCompression(void)
{
}
std::vector<uint8_t> DKRCompression::compressBuffer(std::vector<uint8_t>& data)
{
int uncompressedSize = data.size();
int compressedSize = 0;
uint8_t* compressed = dkr_gzip_compress(&data[0], uncompressedSize, 9, &compressedSize);
std::vector<uint8_t> dkr_compressed(compressed + 10, compressed + compressedSize - 8);
dkr_compressed.insert(dkr_compressed.begin(), 0x09); // gzip compression level?
dkr_compressed.insert(dkr_compressed.begin(), (uncompressedSize >> 24) & 0xFF);
dkr_compressed.insert(dkr_compressed.begin(), (uncompressedSize >> 16) & 0xFF);
dkr_compressed.insert(dkr_compressed.begin(), (uncompressedSize >> 8) & 0xFF);
dkr_compressed.insert(dkr_compressed.begin(), uncompressedSize & 0xFF);
while((dkr_compressed.size() & 0xF) != 0) {
dkr_compressed.push_back(0); // Pad out compressed file to be 16-byte aligned.
}
return dkr_compressed;
}
std::vector<uint8_t> DKRCompression::decompressBuffer(std::vector<uint8_t>& data)
{
compressed.SetCompressedBuffer(&data[0], data.size());
int fileSize = 0, fileSizeCompressed = 0;
uint8_t* decompressed = compressed.OutputDecompressedBuffer(fileSize, fileSizeCompressed);
if(decompressed == NULL) {
std::cout << "Error decompressing input" << std::endl;
throw 1;
}
std::vector<uint8_t> out(decompressed, decompressed + fileSize);
delete[] decompressed;
return out;
}
bool DKRCompression::writeBinaryFile(std::vector<uint8_t>& output, std::string filename)
{
try
{
std::ofstream wf(filename.c_str(), std::ios::out | std::ios::binary);
for(int i = 0; i < output.size(); i++)
wf.write((char *)&output[i], 1);
wf.close();
}
catch (int e)
{
return false;
}
return true;
}
bool DKRCompression::readBinaryFile(std::vector<uint8_t>& input, std::string filename)
{
try
{
std::ifstream is;
is.open(filename.c_str(), std::ios::binary);
is.seekg(0, std::ios::end);
size_t filesize = is.tellg();
is.seekg(0, std::ios::beg);
input.resize(filesize);
is.read((char *)input.data(), filesize);
is.close();
}
catch (int e)
{
return false;
}
return true;
}
@@ -1,27 +0,0 @@
#pragma once
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdint>
#include "GECompression.h"
#include "DKRGzip.h"
// C++17
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
class DKRCompression
{
public:
DKRCompression();
~DKRCompression();
std::vector<uint8_t> compressBuffer(std::vector<uint8_t>& data);
std::vector<uint8_t> decompressBuffer(std::vector<uint8_t>& data);
bool readBinaryFile(std::vector<uint8_t>& input, std::string filename);
bool writeBinaryFile(std::vector<uint8_t>& output, std::string filename);
private:
GECompression compressed;
};
File diff suppressed because it is too large Load Diff
-25
View File
@@ -1,25 +0,0 @@
#pragma once
/*
* This is a custom version of gzip that was made specifically for the
* Diddy Kong Racing decompilation project, since I cannot get zlib to
* produce matching compressed files.
*
* The main difference with this version of gzip is that you can pass in
* a simple byte array instead of having to use the FILE structure.
*
* This library can only compress files at the moment.
*
* Feel free to use it for your own project. The license is the same as gzip.
*/
#ifdef __cplusplus
extern "C"
{
#endif
unsigned char* dkr_gzip_compress(unsigned char* input, int inputSize, int gzipLevel, int* outputSize);
#ifdef __cplusplus
} // extern "C"
#endif
File diff suppressed because it is too large Load Diff
-121
View File
@@ -1,121 +0,0 @@
// GE Recompression C++ Class
// Painstakingly done by Henry Ford (mrhtford_ps2dev@hotmail.com)
// Ported to C++ Class by SubDrag (subdrag@rarewitchproject.com) July 13, 2005
// Compression Games Added April 03, 2007
#pragma once
#include <iostream>
#include <string>
#define maxByteSize 0x800000
#define GOLDENEYE 0
#define PD 1
#define BANJOKAZOOIE 2
#define KILLERINSTINCT 3
#define DONKEYKONG64 4
#define BLASTCORPS 5
#define BANJOTOOIE 6
#define DONKEYKONG64KIOSK 7
#define CONKER 8
#define TOPGEARRALLY 9
#define MILO 10
#define JFG 11
#define DKR 12
#define JFGKIOSK 13
#define MICKEYSPEEDWAY 14
#define MORTALKOMBAT 15
#define STUNTRACER64 16
#define ZLB 17
#define RESIDENTEVIL2 18
#define BOOL bool
#define TRUE true
#define FALSE false
#define DWORD int
#define CString std::string
#define PTSTR std::string
#define LPSTR std::string
struct tableEntry {
unsigned long bits;
unsigned char flags;
int nextIndex;
unsigned long wordValue;
};
class GECompression
{
public:
GECompression();
~GECompression(void);
void SetCompressedBuffer(unsigned char* Buffer, int bufferSize);
unsigned char* OutputDecompressedBuffer(int& fileSize, int& compressedSize);
//bool CompressGZipFile(CString inputFile, CString outputFile, bool byteFlipCompressed);
int game;
CString mainFolder;
void SetGame(int replaceGame);
//void SetPath(CString directory);
//old stuff
//unsigned char* Compress(int& compressedSize);
//unsigned char* OutputCompressed(int compressionType, int& fileSize);
//void SetInputBuffer(unsigned char* Buffer, int bufferSize);
BOOL hiddenExec (PTSTR pCmdLine, CString currentDirectory);
BOOL IsFileExist(LPSTR lpszFilename);
private:
static unsigned char bt1Table1[288];
static unsigned char bt1Table2[30];
static unsigned short bt12Table1S[0x20];
static unsigned short bt12Table1B[0x20];
static unsigned short bt12Table2S[0x20];
static unsigned short bt12Table2B[0x20];
static unsigned char bt2Table1B[0x13];
tableEntry* unpackTable;
int unpackTableIndex;
unsigned long bitsCache;
int bitsRemain;
unsigned long bytesIndex;
//unsigned char* inputBuffer;
//int inputBufferSize;
unsigned char* compressedBuffer;
int compressedBufferSize;
unsigned long GetNBits(int nBits);
unsigned long GetNBitsAndPreserve(int nBits);
bool UncompressType0(unsigned char* returnBuffer, int& fileSize);
bool UncompressType1(unsigned char* returnBuffer, int& fileSize);
bool UncompressType2(unsigned char* returnBuffer, int& fileSize);
void CreateGlobalDecompressionTable(int bit1TableChoice, int size2, int bit12STableSChoice, int bit12BTableChoice, int numBits, bool& returnValue, int& numReturnBits, int& returnStartIndex);
bool DecompressBasedOnTable(int startIndex1, int startIndex2, int bitLen1, int bitLen2, unsigned char* returnBuffer, int& fileSize);
unsigned char variableTable[0x13];
unsigned short tableSize;
unsigned char fiveBits;
unsigned long* wordTable;
// old stuff
/*void FlipBits(unsigned char* indexes, int sizeBytes, int offset);
bool* StringToBits(CString bitString, int& lengthBool);
static CString dictionaryFirstStr[0x10];
static CString dictionarySecondStr[0x10];
CString EncodeLZBitPart(int backAmt);
CString EncodeRLEBitPart(int repeatAmt);
CString HexToBits(int hexNum, int outLength);
CString FlipStringBits(CString inputString);*/
// better compression way
unsigned long iterationCounter;
};
-98
View File
@@ -1,98 +0,0 @@
#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
class ThreadPool {
public:
ThreadPool(size_t);
template<class F, class... Args>
auto enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>;
~ThreadPool();
private:
// need to keep track of threads so we can join them
std::vector< std::thread > workers;
// the task queue
std::queue< std::function<void()> > tasks;
// synchronization
std::mutex queue_mutex;
std::condition_variable condition;
bool stop;
};
// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads)
: stop(false)
{
for(size_t i = 0;i<threads;++i)
workers.emplace_back(
[this]
{
for(;;)
{
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock,
[this]{ return this->stop || !this->tasks.empty(); });
if(this->stop && this->tasks.empty())
return;
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
}
);
}
// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>
{
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared< std::packaged_task<return_type()> >(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
// don't allow enqueueing after stopping the pool
if(stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace([task](){ (*task)(); });
}
condition.notify_one();
return res;
}
// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for(std::thread &worker: workers)
worker.join();
}
#endif
-308
View File
@@ -1,308 +0,0 @@
#include "config.h"
ExtractConfig::ExtractConfig(std::string configsDirectory, std::string filename, std::string outDirectory){
std::cout << "Reading config \"" << filename << "\"" << std::endl;
configJSON = json::JSON::Load(read_file(configsDirectory + '/' + filename));
this->outDirectory = outDirectory + "/assets/" + configJSON["subfolder"].ToString();
this->name = configJSON["config-name"].ToString();
this->subfolder = configJSON["subfolder"].ToString();
this->md5 = configJSON["checksum-md5"].ToString();
this->notSupported = configJSON["not-supported"].ToString() == "true";
}
ExtractConfig::~ExtractConfig(){
}
bool is_binary_type(std::string type) {
return type == "Binary" || type == "GameText" || type == "MenuText" || type == "Sprites" || type == "Miscellaneous"
|| type == "LevelHeaders" || type == "LevelNames" || type == "ObjectHeaders"
|| type == "Audio" || type == "Particles" || type == "ParticleBehaviors" || type == "TTGhosts";
}
bool is_compressed_type(std::string type) {
return type == "Compressed" || type == "LevelObjectMap" || type == "LevelModels"
|| type == "ObjectModels" || type == "ObjectAnimations";
}
std::string get_extension_from_type(std::string type) {
if(is_compressed_type(type)) {
return ".cbin";
} else if(type == "Textures") {
return ".png";
} else {
return ".bin";
}
}
void ExtractConfig::extract_file(ROM& rom, std::string type, std::string folder, std::string filename, std::vector<uint8_t> data) {
}
uint32_t get_uint_from_table(std::vector<uint8_t>& table, int index) {
return (table[index * 4] << 24) | (table[index * 4 + 1] << 16) | (table[index * 4 + 2] << 8) | table[index * 4 + 3];
}
std::vector<std::vector<uint8_t>> get_section_files(std::vector<uint8_t>& section, std::vector<uint8_t>& table, std::string tableType, json::JSON& outputAsset) {
std::vector<std::vector<uint8_t>> out;
int currentTableIndex = 0;
int scale = 1; // Each table entry is 4-bytes long.
int offset = 0;
if(tableType == "MenuText") {
outputAsset["text-entry-count"] = get_uint_from_table(table, currentTableIndex);
currentTableIndex = 1; // The first entry is the number of text entries for each language.
} else if(tableType == "TTGhosts") {
scale = 2; // Each table entry is 8-bytes long.
offset = 1; // offset by 4 bytes.
outputAsset["meta"] = json::Array();
} else if (tableType == "GameText") {
outputAsset["textTypes"] = json::Array();
}
uint32_t currentTableValue = get_uint_from_table(table, currentTableIndex * scale + offset);
uint32_t nextTableValue = get_uint_from_table(table, (currentTableIndex + 1) * scale + offset);
if (tableType == "Audio") {
// The audio table doesn't start at offset 0, so this check will extract
// the first actual part of the audio section.
std::vector<uint8_t> file(section.begin(), section.begin() + currentTableValue);
out.push_back(file);
}
while(nextTableValue != 0xFFFFFFFF) {
if(tableType == "GameText") {
bool isDialog = (currentTableValue & 0x80000000) == 0x80000000;
outputAsset["textTypes"].append(isDialog ? 1 : 0);
currentTableValue &= 0x7FFFFFFF;
nextTableValue &= 0x7FFFFFFF;
} else if(tableType == "Miscellaneous") {
currentTableValue *= 4;
nextTableValue *= 4;
} else if (tableType == "TTGhosts") {
json::JSON metaObj = json::Object();
metaObj["levelID"] = table[currentTableIndex * 8 + 0];
metaObj["vehicleID"] = table[currentTableIndex * 8 + 1];
outputAsset["meta"].append(metaObj);
}
int length = nextTableValue - currentTableValue;
if(length < 0) {
std::cout << "Error: Invalid table length: " << length << std::endl;
std::cout << std::hex << "currentTableValue: " << currentTableValue << std::dec << std::endl;
std::cout << std::hex << "nextTableValue: " << nextTableValue << std::dec << std::endl;
throw 1;
}
std::vector<uint8_t> file(section.begin() + currentTableValue, section.begin() + nextTableValue);
out.push_back(file);
currentTableIndex++;
currentTableValue = get_uint_from_table(table, currentTableIndex * scale + offset);
nextTableValue = get_uint_from_table(table, (currentTableIndex + 1) * scale + offset);
}
return out;
}
void ExtractConfig::extract(ROM& rom, json::JSON& assetsJson) {
currentROMOffset = 0;
/* Extract the code sections */
json::JSON codeSections = configJSON["code"]["sections"];
int numCodeSections = codeSections.length();
for(int i = 0; i < numCodeSections; i++) {
json::JSON section = codeSections[i];
json::JSON files = section["files"];
std::string folder = this->outDirectory + "/" + section["folder"].ToString();
if(!fs::is_directory(folder)) {
fs::create_directories(folder);
}
std::string type = section["type"].ToString();
int numFiles = files.length();
for(int j = 0; j < numFiles; j++) {
json::JSON file = files[j];
int length = std::stoul(file["length"].ToString(), nullptr, 16);
extractions.push_back(ExtractInfo(type, folder, file["filename"].ToString(),
rom.get_bytes_from_range(currentROMOffset, length)));
currentROMOffset += length;
}
}
/* Make sure the assets section has the correct number of entries. */
json::JSON assetSections = configJSON["assets"]["sections"];
int numAssetSections = assetSections.length();
int numAssetSectionsFromTable = rom.get_uint(currentROMOffset);
if(numAssetSections != numAssetSectionsFromTable) {
std::cout << "Error: Number of asset sections in config does not match ROM." << std::endl;
std::cout << "Number of asset sections in Config: " << numAssetSections << std::endl;
std::cout << "Number of asset sections in ROM: " << numAssetSectionsFromTable << std::endl;
throw 1;
}
currentROMOffset += 4;
int tableOffset = currentROMOffset;
currentROMOffset += (numAssetSections + 1) * 4;
int assetsStart = currentROMOffset;
std::vector<std::vector<uint8_t>> sectionsData;
/* Read the main assets look-up table for section lengths */
for(int i = 0; i < numAssetSections; i++) {
int assetSectionLength = rom.get_uint(tableOffset + ((i + 1) * 4)) - rom.get_uint(tableOffset + (i * 4));
sectionsData.push_back(rom.get_bytes_from_range(currentROMOffset, assetSectionLength));
currentROMOffset += assetSectionLength;
}
currentROMOffset = assetsStart;
assetsJson["assets"] = json::Array();
/* Extract the assets sections */
for(int i = 0; i < numAssetSections; i++) {
json::JSON section = assetSections[i];
std::string folder = this->outDirectory + "/" + section["folder"].ToString();
std::string type = section["type"].ToString();
if(!fs::is_directory(folder)) {
fs::create_directories(folder);
}
json::JSON outputAsset = json::Object();
outputAsset["type"] = type;
if(section.hasKey("name")) {
outputAsset["name"] = section["name"].ToString();
}
if(type == "Table") {
assetsJson["assets"].append(outputAsset);
continue;
} else if(type == "Textures") {
outputAsset["flip-textures"] = section["flip-textures"];
}
outputAsset["folder"] = section["folder"].ToString();
outputAsset["filenames"] = json::Array();
if(section.hasKey("table")) {
int tableIndex = section["table"].ToInt();
std::vector<std::vector<uint8_t>> sectionFiles
= get_section_files(sectionsData[i], sectionsData[tableIndex], type, outputAsset);
int numberOfSectionFiles = sectionFiles.size();
outputAsset["table"] = tableIndex;
for(int j = 0; j < numberOfSectionFiles; j++) {
std::string filename = "";
if(section.hasKey("filenames")) {
filename = section["filenames"][j].ToString();
}
if(filename == "") {
std::stringstream filenameStream;
filenameStream << "unknown_" << i << "_" << j;
filename = filenameStream.str();
}
outputAsset["filenames"].append(filename + get_extension_from_type(type));
extractions.push_back(ExtractInfo(type, folder, filename, sectionFiles[j]));
}
} else {
// No lookup table associated, so the section is assumed to be 1 file.
std::string filename = "";
if(section.hasKey("filenames")) {
filename = section["filenames"][0].ToString();
}
if(filename == "") {
std::stringstream filenameStream;
filenameStream << "unknown_" << i;
filename = filenameStream.str();
}
outputAsset["filenames"].append(filename + get_extension_from_type(type));
extractions.push_back(ExtractInfo(type, folder, filename, sectionsData[i]));
}
currentROMOffset += sectionsData[i].size();
assetsJson["assets"].append(outputAsset);
}
ThreadPool pool(std::thread::hardware_concurrency());
for(int i = 0; i < extractions.size(); i++) {
std::string type = extractions[i].type;
std::string folder = extractions[i].folder;
std::string filename = extractions[i].filename;
std::vector<uint8_t> data = extractions[i].data;
pool.enqueue([&rom, type, folder, filename, data] {
std::string outFilepath = folder + "/" + filename;
if(is_binary_type(type)) {
ExtractBinary(data, rom, outFilepath + ".bin");
} else if(is_compressed_type(type)) {
ExtractCompressed(data, rom, outFilepath + ".cbin");
} else if(type == "Textures") {
ExtractTextures(data, rom, outFilepath + ".png");
} else if(type == "Empty") {
} else if(type != "NoExtract") {
std::cout << "Unknown extraction type: " << type << std::endl;
throw 1;
}
});
}
}
bool ExtractConfig::is_supported(){
return !notSupported;
}
std::string ExtractConfig::get_name(){
return name;
}
std::string ExtractConfig::get_md5(){
return md5;
}
std::string ExtractConfig::get_subfolder(){
return subfolder;
}
std::string ExtractConfig::read_file(std::string filename) {
std::ifstream t(filename);
t.seekg(0, std::ios::end);
size_t size = t.tellg();
std::string buffer(size, ' ');
t.seekg(0);
t.read(&buffer[0], size);
return buffer;
}
-60
View File
@@ -1,60 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <regex>
#include <thread>
// C++17
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#include "../json/json.hpp"
#include "rom.h"
#include "extract_binary.h"
#include "extract_compressed.h"
#include "extract_textures.h"
#include "ThreadPool.h"
struct ExtractInfo {
std::string type;
std::string folder;
std::string filename;
std::vector<uint8_t> data;
ExtractInfo(std::string type, std::string folder, std::string filename, std::vector<uint8_t> data)
: type(type), folder(folder), filename(filename), data(data) {}
};
class ExtractConfig {
public:
ExtractConfig(std::string configsDirectory, std::string filename, std::string outDirectory);
~ExtractConfig();
void extract(ROM& rom, json::JSON& assetsJson);
void extract_file(ROM& rom, std::string type, std::string folder, std::string filename, std::vector<uint8_t> data);
bool is_supported();
std::string get_name();
std::string get_md5();
std::string get_subfolder();
private:
json::JSON configJSON; // Loaded from a config file.
int currentROMOffset;
bool notSupported;
std::string outDirectory;
std::string name;
std::string md5;
std::string subfolder;
std::vector<ExtractInfo> extractions;
std::string read_file(std::string filename);
};
-33
View File
@@ -1,33 +0,0 @@
#include "extract.h"
Extract::Extract(std::vector<uint8_t> data, ROM& rom, std::string outFilepath){
}
Extract::~Extract(){
}
void Extract::write_binary_file(std::vector<uint8_t> data, std::string filepath){
std::ofstream wf(filepath.c_str(), std::ios::out | std::ios::binary);
for(int i = 0; i < data.size(); i++)
wf.write((char *)&data[i], 1);
wf.close();
}
void Extract::write_text_file(std::string text, std::string filepath){
std::ofstream myfile;
myfile.open(filepath);
myfile << text;
myfile.close();
}
void Extract::print_extracted(std::string outFilepath) {
std::stringstream out;
out << "Extracted " << outFilepath << std::endl;
std::cout << out.str();
}
void Extract::to_lowercase(std::string& input) {
for(char& character : input) {
character = std::tolower(character);
}
}
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <vector>
#include "rom.h"
#include "../json/json.hpp"
// C++17
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
class Extract {
public:
Extract(std::vector<uint8_t> data, ROM& rom, std::string outFilepath);
~Extract();
void write_binary_file(std::vector<uint8_t> data, std::string filepath);
void write_text_file(std::string data, std::string filepath);
void print_extracted(std::string outFilepath);
protected:
void to_lowercase(std::string& input);
};
@@ -1,10 +0,0 @@
#include "extract_binary.h"
ExtractBinary::ExtractBinary(std::vector<uint8_t> data, ROM& rom, std::string outFilepath)
: Extract(data, rom, outFilepath) {
write_binary_file(data, outFilepath);
print_extracted(outFilepath);
}
ExtractBinary::~ExtractBinary(){
}
@@ -1,9 +0,0 @@
#pragma once
#include "extract.h"
class ExtractBinary : Extract {
public:
ExtractBinary(std::vector<uint8_t> data, ROM& rom, std::string outFilepath);
~ExtractBinary();
};
@@ -1,29 +0,0 @@
#include "extract_compressed.h"
ExtractCompressed::ExtractCompressed(std::vector<uint8_t> data, ROM& rom, std::string outFilepath)
: Extract(data, rom, outFilepath) {
if(data.size() == 0) {
std::cout << "Warning: \"" << outFilepath << "\" is empty." << std::endl;
write_binary_file(data, outFilepath);
print_extracted(outFilepath);
return;
}
// Needed padding to prevent errors with decompressing.
if(data[data.size() - 1] != 0) {
data.push_back(0);
data.push_back(0);
data.push_back(0);
data.push_back(0);
}
DKRCompression compression;
data = compression.decompressBuffer(data);
write_binary_file(data, outFilepath);
print_extracted(outFilepath);
}
ExtractCompressed::~ExtractCompressed(){
}
@@ -1,10 +0,0 @@
#pragma once
#include "extract.h"
#include "../dkr_decompressor_src/DKRCompression.h"
class ExtractCompressed : Extract {
public:
ExtractCompressed(std::vector<uint8_t> data, ROM& rom, std::string outFilepath);
~ExtractCompressed();
};
@@ -1,198 +0,0 @@
#include "extract_textures.h"
ExtractTextures::ExtractTextures(std::vector<uint8_t> data, ROM& rom, std::string outFilepath)
: Extract(data, rom, outFilepath) {
std::vector<uint8_t> header(data.begin(), data.begin() + TEX_HEADER_SIZE);
std::vector<uint8_t> texData;
bool isCompressed = (header[0x1D] == 0x01);
if(isCompressed) {
std::vector<uint8_t> compressedData(data.begin() + TEX_HEADER_SIZE, data.end());
// Needed padding to prevent errors with decompressing.
if(compressedData[compressedData.size() - 1] != 0) {
compressedData.push_back(0);
compressedData.push_back(0);
compressedData.push_back(0);
compressedData.push_back(0);
}
DKRCompression compression;
texData = compression.decompressBuffer(compressedData);
} else {
texData = data;
}
int numTextures = header[0x12];
int dataOffset = 0;
std::vector<uint8_t> combinedTexturesData;
int totalHeight = 0;
for(int i = 0; i < numTextures; i++){
int width = texData[dataOffset + 0x00];
int height = texData[dataOffset + 0x01];
int textureFormat = texData[dataOffset + 0x02] & 0xF;
int textureSize = get_texture_size(width, height, textureFormat);
std::vector<uint8_t> texHeader(texData.begin() + dataOffset, texData.begin() + dataOffset + TEX_HEADER_SIZE);
std::vector<uint8_t> texData2(texData.begin() + dataOffset + TEX_HEADER_SIZE, texData.begin() + dataOffset + textureSize);
process_texture(texHeader, texData2);
combinedTexturesData.insert(combinedTexturesData.end(), texData2.begin(), texData2.end());
dataOffset += textureSize;
totalHeight += texHeader[1];
}
int width = header[0];
int height = totalHeight;
int textureFormat = header[0x02] & 0xF;
switch(textureFormat) {
case TEX_FORMAT_RGBA32:
{
rgba2png(outFilepath.c_str(), (const rgba*)&combinedTexturesData[0], width, height);
break;
}
case TEX_FORMAT_RGBA16:
{
rgba* outTex = raw2rgba(&combinedTexturesData[0], width, height, 16);
rgba2png(outFilepath.c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_I8:
{
ia* outTex = raw2i(&combinedTexturesData[0], width, height, 8);
ia2png(outFilepath.c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_I4:
{
ia* outTex = raw2i(&combinedTexturesData[0], width, height, 4);
ia2png(outFilepath.c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_IA16:
{
ia* outTex = raw2ia(&combinedTexturesData[0], width, height, 16);
ia2png(outFilepath.c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_IA8:
{
ia* outTex = raw2ia(&combinedTexturesData[0], width, height, 8);
ia2png(outFilepath.c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_IA4:
{
ia* outTex = raw2ia(&combinedTexturesData[0], width, height, 4);
ia2png(outFilepath.c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_CI4:
{
std::cout << "Error: CI4 texture format is not currently supported." << std::endl;
throw 1;
}
default: // Invalid texture format
{
std::cout << "Error: Unknown texture format " << textureFormat << std::endl;
throw 1;
}
}
write_binary_file(header, outFilepath + ".header");
print_extracted(outFilepath);
}
ExtractTextures::~ExtractTextures(){
}
void ExtractTextures::deinterlace(std::vector<uint8_t>& data, int width, int height, int bitDepth, int bufferSize) {
uint8_t* temp = new uint8_t[bufferSize];
int numPixels;
if(bitDepth == 4) {
numPixels = ((width * height) / 2);
} else {
numPixels = (width * height * (bitDepth / 8));
}
int stride = bufferSize * 2;
int size = numPixels / stride;
for(int i = 0; i < size; i++) {
int row;
if(bitDepth == 4) {
row = (i * stride) / width * 2;
} else {
row = (i * stride) / width / (bitDepth / 8);
}
if(row % 2 == 0) continue;
for(int j = 0; j < bufferSize; j++) {
temp[j] = data[i * stride + j];
data[i * stride + j] = data[i * stride + j + bufferSize];
data[i * stride + j + bufferSize] = temp[j];
}
}
delete[] temp;
}
void ExtractTextures::process_texture(std::vector<uint8_t>& header, std::vector<uint8_t>& data) {
int width = header[0];
int height = header[1];
int textureFormat = header[0x02] & 0xF;
bool isInterlaced = ((header[0x06] & 0x04) == 0x04);
switch(textureFormat) {
case TEX_FORMAT_RGBA32:
{
if(isInterlaced) deinterlace(data, width, height, 32, 8);
break;
}
case TEX_FORMAT_RGBA16:
case TEX_FORMAT_IA16:
{
if(isInterlaced) deinterlace(data, width, height, 16, 4);
break;
}
case TEX_FORMAT_I8:
case TEX_FORMAT_IA8:
{
if(isInterlaced) deinterlace(data, width, height, 8, 4);
break;
}
case TEX_FORMAT_I4:
case TEX_FORMAT_IA4:
{
if(isInterlaced) deinterlace(data, width, height, 4, 4);
break;
}
}
}
int ExtractTextures::get_texture_size(int width, int height, int textureFormat) {
switch(textureFormat) {
case TEX_FORMAT_RGBA32:
return (width * height * 4) + TEX_HEADER_SIZE;
case TEX_FORMAT_RGBA16:
case TEX_FORMAT_IA16:
return (width * height * 2) + TEX_HEADER_SIZE;
case TEX_FORMAT_I8:
case TEX_FORMAT_IA8:
return (width * height) + TEX_HEADER_SIZE;
case TEX_FORMAT_I4:
case TEX_FORMAT_IA4:
return (width * height / 2) + TEX_HEADER_SIZE;
case TEX_FORMAT_CI4:
std::cout << "Error: CI4 texture format is not currently supported." << std::endl;
throw 1;
}
std::cout << "Error: Invalid texture format " << textureFormat << std::endl;
throw 1;
}
@@ -1,28 +0,0 @@
#pragma once
#include "extract.h"
#include "../dkr_decompressor_src/DKRCompression.h"
#include "../n64graphics/n64graphics.h"
#define TEX_FORMAT_RGBA32 0
#define TEX_FORMAT_RGBA16 1
#define TEX_FORMAT_I8 2
#define TEX_FORMAT_I4 3
#define TEX_FORMAT_IA16 4
#define TEX_FORMAT_IA8 5
#define TEX_FORMAT_IA4 6
#define TEX_FORMAT_CI4 7
#define TEX_HEADER_SIZE 0x20
class ExtractTextures : Extract {
public:
ExtractTextures(std::vector<uint8_t> data, ROM& rom, std::string outFilepath);
~ExtractTextures();
private:
void deinterlace(std::vector<uint8_t>& data, int width, int height, int bitDepth, int bufferSize);
void process_texture(std::vector<uint8_t>& header, std::vector<uint8_t>& data);
int get_texture_size(int width, int height, int textureFormat);
};
-142
View File
@@ -1,142 +0,0 @@
#include "rom.h"
ROM::ROM(std::string filename){
if(!readROMFile(filename)) {
// ROM failed to load.
throw 1;
}
romFilename = filename;
test_endianness();
calculate_md5();
}
ROM::~ROM(){
}
std::vector<uint8_t> ROM::get_bytes_from_range(int start, int numBytes){
std::vector<uint8_t> subVector(numBytes);
std::copy(bytes.begin() + start, bytes.begin() + start + numBytes, subVector.begin());
return subVector;
}
uint8_t ROM::get_byte(int romOffset){
return bytes[romOffset];
}
uint32_t ROM::get_uint(int romOffset){
return (bytes[romOffset] << 24) | (bytes[romOffset + 1] << 16) | (bytes[romOffset + 2] << 8) | bytes[romOffset + 3];
}
std::string ROM::get_md5(){
return md5;
}
int ROM::get_size(){
return bytes.size();
}
void ROM::test_endianness(){
bool convertedToBigEndian = false;
if(bytes[0] == 0x80 && bytes[1] == 0x37) { // Big endian
// Do nothing
} else if(bytes[0] == 0x37 && bytes[1] == 0x80) { // Mixed endian (byteswapped)
// Mixed endian, convert to big endian
int numBytes = get_size();
for(int i = 0; i < numBytes; i+=2) {
uint8_t temp = bytes[i];
bytes[i] = bytes[i + 1];
bytes[i + 1] = temp;
}
convertedToBigEndian = true;
} else if(bytes[0] == 0x40 && bytes[1] == 0x12) { // Little endian
// Little endian, convert to big endian
int numBytes = get_size();
uint8_t temp[4];
for(int i = 0; i < numBytes; i+=4) {
temp[0] = bytes[i + 0];
temp[1] = bytes[i + 1];
temp[2] = bytes[i + 2];
temp[3] = bytes[i + 3];
bytes[i + 0] = temp[3];
bytes[i + 1] = temp[2];
bytes[i + 2] = temp[1];
bytes[i + 3] = temp[0];
}
convertedToBigEndian = true;
} else {
// Invalid ROM file
throw 1;
}
if(convertedToBigEndian) { // Overwrite ROM file
fs::remove(fs::path(romFilename)); // Delete old romfile
std::string z64Filename = romFilename.substr(0, romFilename.length() - 4) + ".z64";
if(!writeROMFile(z64Filename)) { // Save new big-endian rom.
// ROM failed to save.
throw 1;
}
}
}
void ROM::calculate_md5() {
uint8_t buffer[0x4000];
uint8_t digest[MD5_DIGEST_LENGTH];
std::stringstream ss;
MD5_CTX md5Context;
MD5_Init(&md5Context);
MD5_Update(&md5Context, &bytes[0], bytes.size());
int res = MD5_Final(digest, &md5Context);
if(res == 0){ // hash failed or raise an exception
md5 = "";
return;
}
// set up stringstream format
ss << std::hex << std::setfill('0');
for(uint8_t uc: digest) {
ss << std::setw(2) << (int)uc;
}
md5 = ss.str();
}
bool ROM::readROMFile(std::string filename) {
try
{
std::ifstream is;
is.open(filename.c_str(), std::ios::binary);
is.seekg(0, std::ios::end);
size_t filesize = is.tellg();
is.seekg(0, std::ios::beg);
bytes.resize(filesize);
is.read((char *)bytes.data(), filesize);
is.close();
}
catch (int e)
{
return false;
}
return true;
}
bool ROM::writeROMFile(std::string filename) {
try
{
std::ofstream wf(filename.c_str(), std::ios::out | std::ios::binary);
for(int i = 0; i < bytes.size(); i++)
wf.write((char *)&bytes[i], 1);
wf.close();
}
catch (int e)
{
return false;
}
return true;
}
-34
View File
@@ -1,34 +0,0 @@
#pragma once
#include <fstream>
#include <string>
#include <vector>
#include <iomanip>
#include <openssl/md5.h>
// C++17
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
class ROM {
public:
ROM(std::string filename);
~ROM();
std::vector<uint8_t> get_bytes_from_range(int start, int numBytes);
uint8_t get_byte(int romOffset);
uint32_t get_uint(int romOffset);
std::string get_md5();
int get_size();
private:
void test_endianness();
void calculate_md5();
bool readROMFile(std::string filename);
bool writeROMFile(std::string filename);
std::vector<uint8_t> bytes;
std::string md5;
std::string romFilename;
};

Some files were not shown because too many files have changed in this diff Show More