(8.08%) Overhauled assets & decompiled 5 functions in unknown_0777A0

This commit is contained in:
David Benepe
2020-08-15 18:54:40 -05:00
parent d25d1bc2f8
commit 8ef4dd20fa
35 changed files with 5672 additions and 4856 deletions
+36 -439
View File
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
#include "extract.h"
Extract::Extract(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory) :
range(range), rom(rom), assetsJson(assetsJson), outDirectory(outDirectory) {
}
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(int start, int end, std::string subfolder, std::string filename) {
std::cout << "Extracted " << std::setfill('0') << std::setw(6) << std::hex << std::uppercase
<< start << "-" << std::setfill('0') << std::setw(6) << std::hex << std::uppercase << end
<< " as /" << subfolder << "/" << filename << std::endl;
}
void Extract::to_lowercase(std::string& input) {
for(char& character : input) {
character = std::tolower(character);
}
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <vector>
#include "extract_config.h"
#include "rom.h"
#include "../json/json.hpp"
// C++17
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
class Extract {
public:
Extract(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory);
~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(int start, int end, std::string subfolder, std::string name);
protected:
ConfigRange& range;
ROM& rom;
json::JSON& assetsJson;
std::string outDirectory;
void to_lowercase(std::string& input);
};
@@ -0,0 +1,36 @@
#include "extract_binary.h"
ExtractBinary::ExtractBinary(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory) : Extract(range, rom, assetsJson, outDirectory) {
std::string name = range.get_property(1);
std::string outFolder = range.get_property(2);
std::string subfolder = range.get_subfolder();
int startOffset = range.get_start();
int endOffset = startOffset + range.get_size();
std::vector<uint8_t> data = rom.get_bytes_from_range(startOffset, range.get_size());
std::string outputDirectory = outDirectory + "/assets/" + subfolder + "/" + outFolder;
if(!fs::is_directory(outputDirectory)) {
fs::create_directories(outputDirectory);
}
std::stringstream filename;
filename << name << "." << std::setfill('0') << std::setw(6) << std::hex << std::uppercase << startOffset << std::dec << ".bin";
std::stringstream filepath;
filepath << outputDirectory << "/" << filename.str();
write_binary_file(data, filepath.str());
print_extracted(startOffset, endOffset, outFolder, filename.str());
std::string category = range.get_category();
if(category != "none") {
json::JSON obj = json::Object();
obj["filename"] = outFolder + "/" + filename.str();
obj["category"] = category;
assetsJson["assets"].append(obj);
}
}
ExtractBinary::~ExtractBinary(){
}
@@ -0,0 +1,9 @@
#pragma once
#include "extract.h"
class ExtractBinary : Extract {
public:
ExtractBinary(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory);
~ExtractBinary();
};
@@ -0,0 +1,92 @@
#include "extract_cheats.h"
#define get_u16(data, offset) (uint16_t)((data[offset] << 8) | data[offset + 1])
ExtractCheats::ExtractCheats(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory) : Extract(range, rom, assetsJson, outDirectory) {
std::string name = range.get_property(1);
std::string outFolder = range.get_property(2);
std::string subfolder = range.get_subfolder();
int startOffset = range.get_start();
int endOffset = startOffset + range.get_size();
std::string outputDirectory = outDirectory + "/assets/" + subfolder + "/" + outFolder;
if(!fs::is_directory(outputDirectory)) {
fs::create_directories(outputDirectory);
}
std::stringstream filename;
filename << name << "." << std::setfill('0') << std::setw(6) << std::hex << std::uppercase << startOffset << std::dec << ".cheats";
std::stringstream filepath;
filepath << outputDirectory << "/" << filename.str();
std::vector<uint8_t> data = rom.get_bytes_from_range(startOffset, range.get_size());
decrypt_data(data);
std::stringstream outFile;
uint16_t numOfCheats = get_u16(data, 0);
for(int i = 0; i < numOfCheats; i++) {
uint16_t codeWordOffset = get_u16(data, 2 + (i * 4));
uint16_t codeDescriptionOffset = get_u16(data, 2 + (i * 4) + 2);
char* codeWord = (char*)&data[codeWordOffset];
char* codeDescription = (char*)&data[codeDescriptionOffset];
outFile << "\"" << codeWord << "\", \"" << codeDescription << "\"" << std::endl;
}
write_text_file(outFile.str(), filepath.str());
print_extracted(startOffset, endOffset, outFolder, filename.str());
std::string category = range.get_category();
if(category != "none") {
json::JSON obj = json::Object();
obj["filename"] = outFolder + "/" + filename.str();
obj["category"] = category;
assetsJson["assets"].append(obj);
}
}
ExtractCheats::~ExtractCheats(){
}
// Only used for the magic codes.
void ExtractCheats::decrypt_data(std::vector<uint8_t>& data) {
int numWords = data.size() / 4;
int a, b, c, d, sp0, sp1, sp2, sp3;
for(int i = 0; i < numWords; i++) {
a = (data[(i * 4) + 3] & 0xC0) >> 6;
b = (data[(i * 4) + 0] & 0xC0);
c = (data[(i * 4) + 1] & 0xC0) >> 2;
d = (data[(i * 4) + 2] & 0xC0) >> 4;
sp0 = a | b | c | d;
a = (data[(i * 4) + 3] & 0x30) >> 4;
b = (data[(i * 4) + 0] & 0x30) << 2;
c = (data[(i * 4) + 1] & 0x30);
d = (data[(i * 4) + 2] & 0x30) >> 2;
sp1 = a | b | c | d;
a = (data[(i * 4) + 3] & 0x0C) >> 2;
b = (data[(i * 4) + 0] & 0x0C) << 4;
c = (data[(i * 4) + 1] & 0x0C) << 2;
d = (data[(i * 4) + 2] & 0x0C);
sp2 = a | b | c | d;
a = (data[(i * 4) + 3] & 0x03);
b = (data[(i * 4) + 0] ) << 6;
c = (data[(i * 4) + 1] & 0x03) << 4;
d = (data[(i * 4) + 2] & 0x03) << 2;
sp3 = a | b | c | d;
a = (sp0 & 0xAA) >> 1;
b = (sp0 & 0x55) << 1;
data[(i * 4) + 0] = a | b;
a = (sp1 & 0xAA) >> 1;
b = (sp1 & 0x55) << 1;
data[(i * 4) + 1] = a | b;
a = (sp2 & 0xAA) >> 1;
b = (sp2 & 0x55) << 1;
data[(i * 4) + 2] = a | b;
a = (sp3 & 0xAA) >> 1;
b = (sp3 & 0x55) << 1;
data[(i * 4) + 3] = a | b;
}
}
@@ -0,0 +1,11 @@
#pragma once
#include "extract.h"
class ExtractCheats : Extract {
public:
ExtractCheats(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory);
~ExtractCheats();
private:
void decrypt_data(std::vector<uint8_t>& data);
};
@@ -0,0 +1,48 @@
#include "extract_compressed.h"
ExtractCompressed::ExtractCompressed(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory) : Extract(range, rom, assetsJson, outDirectory) {
std::string name = range.get_property(1);
std::string outFolder = range.get_property(2);
std::string subfolder = range.get_subfolder();
int startOffset = range.get_start();
int endOffset = startOffset + range.get_size();
std::vector<uint8_t> data = rom.get_bytes_from_range(startOffset, range.get_size());
// 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);
std::string outputDirectory = outDirectory + "/assets/" + subfolder + "/" + outFolder;
if(!fs::is_directory(outputDirectory)) {
fs::create_directories(outputDirectory);
}
std::stringstream filename;
filename << name << "." << std::setfill('0') << std::setw(6) << std::hex << std::uppercase << startOffset << std::dec << ".cbin";
std::stringstream filepath;
filepath << outputDirectory << "/" << filename.str();
write_binary_file(data, filepath.str());
print_extracted(startOffset, endOffset, outFolder, filename.str());
std::string category = range.get_category();
if(category != "none") {
json::JSON obj = json::Object();
obj["filename"] = outFolder + "/" + filename.str();
obj["category"] = category;
assetsJson["assets"].append(obj);
}
}
ExtractCompressed::~ExtractCompressed(){
}
@@ -0,0 +1,10 @@
#pragma once
#include "extract.h"
#include "../dkr_decompressor_src/DKRCompression.h"
class ExtractCompressed : Extract {
public:
ExtractCompressed(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory);
~ExtractCompressed();
};
+21 -11
View File
@@ -1,10 +1,12 @@
#include "extract_config.h"
ConfigRange::ConfigRange(int start, int size, ConfigRangeType type, std::vector<std::string> properties){
rangeStart = start;
rangeSize = size;
rangeType = type;
rangeProperties = properties;
ConfigRange::ConfigRange(int start, int size, ConfigRangeType type, std::vector<std::string> properties, std::string subfolder, std::string category){
this->rangeStart = start;
this->rangeSize = size;
this->rangeType = type;
this->rangeProperties = properties;
this->subfolder = subfolder;
this->assetCategory = category;
}
ConfigRange::~ConfigRange(){
@@ -13,12 +15,15 @@ ConfigRange::~ConfigRange(){
int ConfigRange::get_start(){
return rangeStart;
}
int ConfigRange::get_size(){
return rangeSize;
}
ConfigRangeType ConfigRange::get_type(){
return rangeType;
}
std::string ConfigRange::get_property(int propertyIndex){
if(propertyIndex >= rangeProperties.size()) {
return "";
@@ -26,6 +31,14 @@ std::string ConfigRange::get_property(int propertyIndex){
return rangeProperties[propertyIndex];
}
std::string ConfigRange::get_subfolder(){
return subfolder;
}
std::string ConfigRange::get_category(){
return assetCategory;
}
/**********************************************/
Config::Config(std::string directory, std::string filename){
@@ -95,20 +108,17 @@ void Config::parse_property(std::string name, std::string value){
if(name == "config-name") {
this->name = value;
return;
} else if (name == "subfolder") {
this->subfolder = value;
return;
} else if (name == "checksum-md5") {
this->md5 = value;
return;
} else if (name == "not-supported") {
this->notSupported = get_lowercase(trim(value)) == "true";
return;
} else if (name == "include") {
std::string text = read_file(this->directory + '/' + value);
parse(text);
return;
} else if (name == "asset-category") {
this->currentAssetCategory = get_lowercase(trim(value));
}
return;
@@ -127,7 +137,7 @@ void Config::parse_range(std::string rangeSize, std::string rangeProperties) {
}
ConfigRangeType type = get_range_type(properties[0]);
ranges.push_back(ConfigRange(currentRangeOffset, size, type, properties));
ranges.push_back(ConfigRange(currentRangeOffset, size, type, properties, this->subfolder, this->currentAssetCategory));
currentRangeOffset += size;
}
+6 -1
View File
@@ -22,16 +22,20 @@ enum ConfigRangeType {
class ConfigRange {
public:
ConfigRange(int start, int size, ConfigRangeType type, std::vector<std::string> properties);
ConfigRange(int start, int size, ConfigRangeType type, std::vector<std::string> properties, std::string subfolder, std::string category);
~ConfigRange();
int get_start();
int get_size();
ConfigRangeType get_type();
std::string get_category();
std::string get_property(int propertyIndex);
std::string get_subfolder();
private:
int rangeStart, rangeSize;
std::string subfolder;
std::string assetCategory;
ConfigRangeType rangeType;
std::vector<std::string> rangeProperties;
};
@@ -68,6 +72,7 @@ private:
std::string md5;
std::string subfolder;
std::vector<ConfigRange> ranges;
std::string currentAssetCategory;
int currentRangeOffset = 0;
};
@@ -0,0 +1,255 @@
#include "extract_textures.h"
ExtractTextures::ExtractTextures(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory) : Extract(range, rom, assetsJson, outDirectory) {
std::string name = range.get_property(1);
std::string outFolder = range.get_property(2);
std::string flipString = range.get_property(3);
to_lowercase(flipString);
bool shouldFlip = (flipString == "flipvertically");
std::string subfolder = range.get_subfolder();
int startOffset = range.get_start();
int endOffset = startOffset + range.get_size();
std::string outputDirectory = outDirectory + "/assets/" + subfolder + "/" + outFolder;
if(!fs::is_directory(outputDirectory)) {
fs::create_directories(outputDirectory);
}
std::stringstream filename;
filename << name << "." << std::setfill('0') << std::setw(6) << std::hex << std::uppercase << startOffset << std::dec;
std::stringstream filepath;
filepath << outputDirectory << "/" << filename.str() << ".png";
std::stringstream header_filepath;
header_filepath << outputDirectory << "/" << filename.str() << ".header";
std::vector<uint8_t> data;
std::vector<uint8_t> header = rom.get_bytes_from_range(startOffset, TEX_HEADER_SIZE);
bool isCompressed = (header[0x1D] == 0x01);
if(isCompressed) {
std::vector<uint8_t> compressedData = rom.get_bytes_from_range(startOffset + TEX_HEADER_SIZE, endOffset - startOffset - TEX_HEADER_SIZE);
// 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;
data = compression.decompressBuffer(compressedData);
} else {
data = rom.get_bytes_from_range(startOffset, endOffset - startOffset);
}
int numTextures = header[0x12];
int dataOffset = 0;
std::vector<uint8_t> combinedTexturesData;
int totalHeight = 0;
for(int i = 0; i < numTextures; i++){
int width = data[dataOffset + 0x00];
int height = data[dataOffset + 0x01];
int textureFormat = data[dataOffset + 0x02] & 0xF;
int textureSize = get_texture_size(width, height, textureFormat);
std::vector<uint8_t> texHeader(data.begin() + dataOffset, data.begin() + dataOffset + TEX_HEADER_SIZE);
std::vector<uint8_t> texData(data.begin() + dataOffset + TEX_HEADER_SIZE, data.begin() + dataOffset + textureSize);
process_texture(texHeader, texData, shouldFlip);
combinedTexturesData.insert(combinedTexturesData.end(), texData.begin(), texData.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(filepath.str().c_str(), (const rgba*)&combinedTexturesData[0], width, height);
break;
}
case TEX_FORMAT_RGBA16:
{
rgba* outTex = raw2rgba(&combinedTexturesData[0], width, height, 16);
rgba2png(filepath.str().c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_I8:
{
ia* outTex = raw2i(&combinedTexturesData[0], width, height, 8);
ia2png(filepath.str().c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_I4:
{
ia* outTex = raw2i(&combinedTexturesData[0], width, height, 4);
ia2png(filepath.str().c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_IA16:
{
ia* outTex = raw2ia(&combinedTexturesData[0], width, height, 16);
ia2png(filepath.str().c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_IA8:
{
ia* outTex = raw2ia(&combinedTexturesData[0], width, height, 8);
ia2png(filepath.str().c_str(), outTex, width, height);
break;
}
case TEX_FORMAT_IA4:
{
ia* outTex = raw2ia(&combinedTexturesData[0], width, height, 4);
ia2png(filepath.str().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, header_filepath.str());
print_extracted(startOffset, endOffset, outFolder, filename.str());
std::string category = range.get_category();
if(category != "none") {
json::JSON obj = json::Object();
obj["filename"] = outFolder + "/" + filename.str() + ".png";
obj["headerFilename"] = outFolder + "/" + filename.str() + ".header";
obj["category"] = category;
obj["compressed"] = isCompressed;
obj["flipVertically"] = shouldFlip;
assetsJson["assets"].append(obj);
}
}
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::flip_vertically(std::vector<uint8_t>& data, int width, int height, int bitDepth) {
int rowSize;
if(bitDepth == 4) {
rowSize = width / 2;
} else {
rowSize = width * (bitDepth / 8);
}
uint8_t temp = 0;
for(int y = 0; y < height/2; y++) {
for(int x = 0; x < rowSize; x++) {
temp = data[y * rowSize + x];
data[y * rowSize + x] = data[(height - y - 1) * rowSize + x];
data[(height - y - 1) * rowSize + x] = temp;
}
}
}
void ExtractTextures::process_texture(std::vector<uint8_t>& header, std::vector<uint8_t>& data, bool shouldFlip) {
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);
//if(shouldFlip) flip_vertically(data, width, height, 32);
break;
}
case TEX_FORMAT_RGBA16:
case TEX_FORMAT_IA16:
{
if(isInterlaced) deinterlace(data, width, height, 16, 4);
//if(shouldFlip) flip_vertically(data, width, height, 16);
break;
}
case TEX_FORMAT_I8:
case TEX_FORMAT_IA8:
{
if(isInterlaced) deinterlace(data, width, height, 8, 4);
//if(shouldFlip) flip_vertically(data, width, height, 8);
break;
}
case TEX_FORMAT_I4:
case TEX_FORMAT_IA4:
{
if(isInterlaced) deinterlace(data, width, height, 4, 4);
//if(shouldFlip) flip_vertically(data, width, height, 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;
}
@@ -0,0 +1,29 @@
#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(ConfigRange& range, ROM& rom, json::JSON& assetsJson, std::string outDirectory);
~ExtractTextures();
private:
void deinterlace(std::vector<uint8_t>& data, int width, int height, int bitDepth, int bufferSize);
void flip_vertically(std::vector<uint8_t>& data, int width, int height, int bitDepth);
void process_texture(std::vector<uint8_t>& header, std::vector<uint8_t>& data, bool shouldFlip);
int get_texture_size(int width, int height, int textureFormat);
};
+36 -150
View File
@@ -24,7 +24,7 @@ const std::string PNG_EXTENSION = ".png";
/*********************************************/
void show_help() {
std::cout << "Usage: ./dkr_texbuilder <input_png_file> <output_compressed_file>" << std::endl;
std::cout << "Usage: ./dkr_texbuilder <input_png_file> <input_header_file> <output_compressed_file>" << std::endl;
}
bool starts_with(std::string filename, std::string pattern) {
@@ -54,6 +54,19 @@ std::vector<std::string> split(const std::string &s, char delim) {
return elems;
}
std::vector<uint8_t> read_binary(std::string filename) {
std::vector<uint8_t> bytes;
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();
return bytes;
}
/*********************************************/
@@ -89,41 +102,6 @@ void deinterlace(std::vector<uint8_t>& data, int width, int height, int bitDepth
delete[] temp;
}
void flip_vertically(std::vector<uint8_t>& data, int width, int height, int bitDepth) {
int rowSize;
if(bitDepth == 4) {
rowSize = width / 2;
} else {
rowSize = width * (bitDepth / 8);
}
uint8_t temp = 0;
for(int y = 0; y < height/2; y++) {
for(int x = 0; x < rowSize; x++) {
temp = data[y * rowSize + x];
data[y * rowSize + x] = data[(height - y - 1) * rowSize + x];
data[(height - y - 1) * rowSize + x] = temp;
}
}
}
int get_texture_type(std::string textureFormatString) {
to_lowercase(textureFormatString);
if(textureFormatString == "rgba32") return TEX_FORMAT_RGBA32;
else if(textureFormatString == "rgba16") return TEX_FORMAT_RGBA16;
else if(textureFormatString == "i8") return TEX_FORMAT_I8;
else if(textureFormatString == "i4") return TEX_FORMAT_I4;
else if(textureFormatString == "ia16") return TEX_FORMAT_IA16;
else if(textureFormatString == "ia8") return TEX_FORMAT_IA8;
else if(textureFormatString == "ia4") return TEX_FORMAT_IA4;
else if(textureFormatString == "ci4") return TEX_FORMAT_CI4;
std::cout << "Error: Invalid texture type: " << textureFormatString << std::endl;
throw 1;
}
std::vector<uint8_t> load_texture_from_png(std::string filepath, int textureFormat, int* width, int* height) {
switch(textureFormat) {
case TEX_FORMAT_RGBA32:
@@ -178,61 +156,6 @@ int get_texture_size(int width, int height, int textureFormat) {
throw 1;
}
void generate_texture_header(std::vector<uint8_t>& outData, int width, int height, int textureFormat,
int numTextures, std::string flags, std::string headerBytes, bool& forceAlignment) {
// I currently do not know what these are for.
int A = std::stoi(headerBytes.substr(0, 1), 0, 16);
int B = std::stoi(headerBytes.substr(1, 2), 0, 16);
int C = std::stoi(headerBytes.substr(3, 2), 0, 16);
int D = std::stoi(headerBytes.substr(5, 4), 0, 16);
int E = std::stoi(headerBytes.substr(9, 2), 0, 16);
bool dontComputeSize = (flags.at(2) == 'Z');
int textureSize = get_texture_size(width, height, textureFormat);
/* Texture header is 0x20 bytes long */
/* 0x00 */ outData.push_back(width);
/* 0x01 */ outData.push_back(height);
/* 0x02 */ outData.push_back((A << 4) | textureFormat);
/* 0x03 */ outData.push_back(B);
/* 0x04 */ outData.push_back(C);
/* 0x05 */ outData.push_back(0x01); // Always 0x01?
/* 0x06 */ outData.push_back((D >> 8) & 0xFF);
outData.push_back(D & 0xFF);
/* 0x08 */ outData.push_back(0); // Offset to CI4 palette
outData.push_back(0);
/* 0x0A */ outData.push_back(0);
/* 0x0B */ outData.push_back(0); // Initalized in RAM; Number of commands in display list.
/* 0x0C */ outData.push_back(0); // Initalized in RAM; Pointer to texture display list
outData.push_back(0);
outData.push_back(0);
outData.push_back(0);
/* 0x10 */ outData.push_back(0);
/* 0x11 */ outData.push_back(0);
/* 0x12 */ outData.push_back(numTextures);
/* 0x13 */ outData.push_back(0);
/* 0x14 */ outData.push_back(0);
/* 0x15 */ outData.push_back(E);
if(dontComputeSize) {
/* 0x16 */ outData.push_back(0); // Keep it zero, Not sure why this is neccessary though.
outData.push_back(0);
forceAlignment = true;
} else {
/* 0x16 */ outData.push_back((textureSize >> 8) & 0xFF); // Texture size
outData.push_back(textureSize & 0xFF);
forceAlignment = false;
}
/* 0x18 */ outData.push_back(0);
/* 0x19 */ outData.push_back(0);
/* 0x1A */ outData.push_back(0);
/* 0x1B */ outData.push_back(0);
/* 0x1C */ outData.push_back(0);
/* 0x1D */ outData.push_back(dontComputeSize ? 1 : 0);
/* 0x1E */ outData.push_back(0);
/* 0x1F */ outData.push_back(0);
}
int get_bitdepth_from_format(int textureFormat){
switch(textureFormat) {
case TEX_FORMAT_RGBA32:
@@ -252,22 +175,19 @@ int get_bitdepth_from_format(int textureFormat){
throw 1;
}
std::vector<uint8_t> get_texture_data(std::vector<uint8_t> pngData, int width, int height, int textureFormat, int numTextures,
std::string flags, std::string headerBytes) {
std::vector<uint8_t> get_texture_data(std::vector<uint8_t> pngData, std::vector<uint8_t> &header, bool forceAlignment) {
std::vector<uint8_t> outData;
bool forceAlignment;
generate_texture_header(outData, width, height, textureFormat, numTextures, flags, headerBytes, forceAlignment);
bool flipVertically = (flags.at(1) == 'F');
outData.insert(outData.end(), header.begin(), header.begin() + TEX_HEADER_SIZE);
bool interlace = ((outData[0x06] & 0x4) == 0x04);
int width = outData[0x00];
int height = outData[0x01];
int textureFormat = outData[0x02] & 0x0F;
switch(textureFormat) {
case TEX_FORMAT_RGBA32:
{
if(flipVertically) {
flip_vertically(pngData, width, height, 32);
}
if (interlace) {
deinterlace(pngData, width, height, 32, 8);
}
@@ -280,9 +200,6 @@ std::string flags, std::string headerBytes) {
rgba2raw(rgba16Data, (const rgba*)&pngData[0], width, height, 16);
std::vector<uint8_t> rgba16(rgba16Data, rgba16Data + (width * height * 2));
free(rgba16Data);
if(flipVertically) {
flip_vertically(rgba16, width, height, 16);
}
if (interlace) {
deinterlace(rgba16, width, height, 16, 4);
}
@@ -295,9 +212,6 @@ std::string flags, std::string headerBytes) {
i2raw(i8Data, (const ia*)&pngData[0], width, height, 8);
std::vector<uint8_t> i8(i8Data, i8Data + (width * height));
free(i8Data);
if(flipVertically) {
flip_vertically(i8, width, height, 8);
}
if (interlace) {
deinterlace(i8, width, height, 8, 4);
}
@@ -310,9 +224,6 @@ std::string flags, std::string headerBytes) {
i2raw(i4Data, (const ia*)&pngData[0], width, height, 4);
std::vector<uint8_t> i4(i4Data, i4Data + (width * height / 2));
free(i4Data);
if(flipVertically) {
flip_vertically(i4, width, height, 4);
}
if (interlace) {
deinterlace(i4, width, height, 4, 4);
}
@@ -325,9 +236,6 @@ std::string flags, std::string headerBytes) {
ia2raw(ia16Data, (const ia*)&pngData[0], width, height, 16);
std::vector<uint8_t> ia16(ia16Data, ia16Data + (width * height * 2));
free(ia16Data);
if(flipVertically) {
flip_vertically(ia16, width, height, 16);
}
if (interlace) {
deinterlace(ia16, width, height, 16, 4);
}
@@ -340,9 +248,6 @@ std::string flags, std::string headerBytes) {
ia2raw(ia8Data, (const ia*)&pngData[0], width, height, 8);
std::vector<uint8_t> ia8(ia8Data, ia8Data + (width * height));
free(ia8Data);
if(flipVertically) {
flip_vertically(ia8, width, height, 8);
}
if (interlace) {
deinterlace(ia8, width, height, 8, 4);
}
@@ -355,9 +260,6 @@ std::string flags, std::string headerBytes) {
ia2raw(ia4Data, (const ia*)&pngData[0], width, height, 4);
std::vector<uint8_t> ia4(ia4Data, ia4Data + (width * height / 2));
free(ia4Data);
if(flipVertically) {
flip_vertically(ia4, width, height, 4);
}
if (interlace) {
deinterlace(ia4, width, height, 4, 4);
}
@@ -375,30 +277,21 @@ std::string flags, std::string headerBytes) {
return outData;
}
std::vector<uint8_t> get_texture_binary(std::string inputFilename, std::string inputFilepath, std::string& flags) {
std::vector<uint8_t> get_texture_binary(std::string inputFilepath, std::string inputHeaderFilepath, bool& isCompressed) {
std::vector<uint8_t> out;
std::vector<std::string> elems = split(inputFilename, '.');
int numElements = elems.size();
std::vector<uint8_t> header = read_binary(inputHeaderFilepath);
int numTextures, textureFormat;
std::string headerBytes;
isCompressed = (header[0x1D] != 0x00);
bool dontComputeSizeInHeader = (header[0x16] == 0x00 && header[0x17] == 0x00);
if(numElements == 6) {
numTextures = 1;
flags = elems[2];
headerBytes = elems[3];
textureFormat = get_texture_type(elems[4]);
} else if(numElements == 7) {
numTextures = stoi(elems[2], 0, 10);
flags = elems[3];
headerBytes = elems[4];
textureFormat = get_texture_type(elems[5]);
} else {
std::cout << "Invalid texture name" << std::endl;
throw 1;
if (!dontComputeSizeInHeader) {
header[0x1D] = 0x00;
}
int textureFormat = header[0x02] & 0xF;
int numTextures = header[0x12];
int width, totalHeight;
std::vector<uint8_t> combinedPngData = load_texture_from_png(inputFilepath, textureFormat, &width, &totalHeight);
@@ -416,7 +309,7 @@ std::vector<uint8_t> get_texture_binary(std::string inputFilename, std::string i
int pngSectionStart = i * pngSize;
int pngSectionEnd = (i + 1) * pngSize;
std::vector<uint8_t> pngData(combinedPngData.begin() + pngSectionStart, combinedPngData.begin() + pngSectionEnd);
std::vector<uint8_t> textureData = get_texture_data(pngData, width, height, textureFormat, numTextures, flags, headerBytes);
std::vector<uint8_t> textureData = get_texture_data(pngData, header, dontComputeSizeInHeader);
out.insert(out.end(), textureData.begin(), textureData.end());
}
@@ -426,30 +319,23 @@ std::vector<uint8_t> get_texture_binary(std::string inputFilename, std::string i
/*********************************************/
int main(int argc, char* argv[]) {
if(argc != 3) {
if(argc != 4) {
show_help();
return 1;
}
std::string inputFilepath = argv[1];
std::string outputFilename = argv[2];
std::string inputFilename;
std::string inputHeaderFilepath = argv[2];
std::string outputFilename = argv[3];
std::size_t lastSlash = inputFilepath.rfind('/');
if(lastSlash == std::string::npos) {
inputFilename = inputFilepath;
} else {
inputFilename = inputFilepath.substr(lastSlash + 1, inputFilepath.length() - lastSlash - 1);
}
std::string flags;
std::vector<uint8_t> uncompressed = get_texture_binary(inputFilename, inputFilepath, flags);
bool isCompressed;
std::vector<uint8_t> uncompressed = get_texture_binary(inputFilepath, inputHeaderFilepath, isCompressed);
// DEBUG
// std::string outFilename = outputFilename + ".uncmp.bin";
// write_binary_file(uncompressed, outFilename);
if(flags.at(0) == 'C') {
if(isCompressed) {
std::vector<uint8_t> compressedHeader;
compressedHeader.insert(compressedHeader.end(), uncompressed.begin(), uncompressed.begin() + TEX_HEADER_SIZE);
+648
View File
File diff suppressed because it is too large Load Diff
+98 -61
View File
@@ -1,25 +1,29 @@
import re
import os
import json
from file_util import FileUtil
# Possible TODO: Move generating assets.s into a seperate python script.
VERSION = 'us_1.0'
LD_NAME = 'dkr.ld'
ASM_DIR = './asm'
SRC_DIR = './src'
LIB_ASM_DIR = './lib/asm'
LIB_SRC_DIR = './lib/src'
ASSETS_S_FILENAME = './asm/assets/assets.s'
ASSETS_UCODE_S_FILENAME = './asm/assets/ucode.s'
ASSETS_DIR = './assets/us_1.0'
ASSETS_FILENAME = './asm/assets/assets.s'
ASSETS_LUT_FILENAME = './asm/assets/assets_lut.s'
ASSETS_DIR = './assets/' + VERSION
ASSETS_START = 0x0D8200
BUILD_DIR = 'build/us_1.0'
UCODE_DIR = './ucode/' + VERSION
UCODE_TEXT_FILENAME = './asm/assets/ucode_text.s'
UCODE_DATA_FILENAME = './asm/assets/ucode_data.s'
BUILD_DIR = 'build/' + VERSION
class LD:
def __init__(self, file):
print('Generating linker file...')
self.generate_assets_file()
self.generate_ucode_files()
self.files = self.get_code_files()
self.indentLevel = 0
self.file = file
@@ -27,25 +31,8 @@ class LD:
self.gen_newline()
self.gen_line('OUTPUT_ARCH (mips)')
self.gen_newline()
#self.gen_macros()
self.gen_sections()
print('New linker file created!')
def gen_macros(self):
self.gen_line('#define BEGIN_SEG(name, addr) \\')
self.increase_indent()
self.gen_line('_##name##SegmentStart = ADDR(.name); \\')
self.gen_line('_##name##SegmentRomStart = __romPos; \\')
self.gen_line('.name addr : AT(__romPos)')
self.decrease_indent()
self.gen_newline()
self.gen_line('#define END_SEG(name) \\')
self.increase_indent()
self.gen_line('_##name##SegmentEnd = ADDR(.name) + SIZEOF(.name); \\')
self.gen_line('_##name##SegmentRomEnd = __romPos + SIZEOF(.name); \\')
self.gen_line('__romPos += SIZEOF(.name);')
self.decrease_indent()
self.gen_newline()
def gen_sections(self):
self.gen_line('SECTIONS')
@@ -53,8 +40,9 @@ class LD:
self.gen_line('romPos = 0x0;')
self.gen_boot_section()
self.gen_main_section()
self.gen_ucode_section()
self.gen_ucode_text_section()
self.gen_data_section()
self.gen_ucode_data_section()
self.gen_assets_section()
self.gen_discard()
self.gen_close_block()
@@ -77,12 +65,12 @@ class LD:
self.gen_line('romPos += SIZEOF(.main);')
self.gen_newline()
def gen_ucode_section(self):
self.gen_line('.ucode 0 : AT(romPos)')
def gen_ucode_text_section(self):
self.gen_line('.ucodeText 0 : AT(romPos)')
self.gen_open_block()
self.gen_line(BUILD_DIR + '/asm/assets/ucode.o(.text);')
self.gen_line(BUILD_DIR + '/asm/assets/ucode_text.o(.text);')
self.gen_close_block()
self.gen_line('romPos += SIZEOF(.ucode);')
self.gen_line('romPos += SIZEOF(.ucodeText);')
self.gen_newline()
def gen_data_section(self):
@@ -93,7 +81,28 @@ class LD:
self.gen_line('romPos += SIZEOF(.main_data);')
self.gen_newline()
def gen_ucode_data_section(self):
self.gen_line('.ucodeData 0 : AT(romPos)')
self.gen_open_block()
self.gen_line(BUILD_DIR + '/asm/assets/ucode_data.o(.text);')
self.gen_close_block()
self.gen_line('romPos += SIZEOF(.ucodeData);')
self.gen_newline()
def gen_assets_section(self):
self.gen_line('__ASSETS_LUT_START = romPos;');
self.gen_newline()
self.gen_line('.assets_lut 0 : AT(romPos)')
self.gen_open_block()
self.gen_line(BUILD_DIR + '/asm/assets/assets_lut.o(.text);')
self.gen_close_block()
self.gen_line('romPos += SIZEOF(.assets_lut);')
self.gen_newline()
self.gen_line('__ASSETS_LUT_END = romPos;');
self.gen_newline()
self.gen_line('.assets 0 : AT(romPos)')
self.gen_open_block()
self.gen_line(BUILD_DIR + '/asm/assets/assets.o(.text);')
@@ -141,7 +150,6 @@ class LD:
def gen_newline(self):
self.file.write('\n')
def append_files(self, files, extensions, directory, outputDir):
filenames = FileUtil.get_filenames_from_directory(directory, extensions)
regex = r'[\/][*]+\s*RAM_POS:\s*0x([0-9a-fA-F]+)\s*[*]+[\/]'
@@ -169,34 +177,27 @@ class LD:
def get_asset_files(self):
assetFiles = []
assetFilenames = FileUtil.getListOfFiles(ASSETS_DIR)
regex = r'[.][\/]assets[\/][^\/]*[\/]([^\/]*[\/][^.]*)[.](([.]?[^.]*)+)([.][A-Za-z0-9_]*)'
for filename in assetFilenames:
matches = re.match(regex, filename)
if matches is None:
raise Exception('Invalid filename: \"' + filename + '"')
matchedGroups = matches.groups()
isMicrocode = False
if matchedGroups[0].startswith('ucode/ucode_'):
isMicrocode = True
fileExtension = matchedGroups[3]
fileProperties = matchedGroups[1].split('.')
ramAddress = fileProperties[0]
if fileExtension == '.cbin' or fileExtension == '.ebin':
outFileExtension = fileExtension
else:
outFileExtension = '.bin'
outFilename = BUILD_DIR + '/' + matchedGroups[0] + '.' + matchedGroups[1] + outFileExtension
if int(ramAddress, 16) >= ASSETS_START:
assetFiles.append((outFilename, ramAddress, fileExtension, isMicrocode))
assetFiles.sort(key = lambda x: x[1]) # Sort tuples by RAM address
return assetFiles
mainLUT = ''
with open(ASSETS_DIR + '/assets.json') as jsonFile:
data = json.load(jsonFile)
for asset in data['assets']:
if asset['category'] == 'main':
if mainLUT != '':
raise Exception('Category "main" was expected to only have 1 item. Please update generate_ld.py.')
mainLUT = BUILD_DIR + '/' + asset['filename']
continue
filename, extension = os.path.splitext(asset['filename'])
if extension == '.cbin' or extension == '.ebin':
filename += extension
else:
filename += '.bin'
assetFiles.append((BUILD_DIR + '/' + filename, extension))
return assetFiles, mainLUT
def generate_assets_file(self):
assets = self.get_asset_files()
assets, mainLUT = self.get_asset_files()
assetsText = '# This file was generated by generate_ld.py\n\n'
assetsText += '.macro .incbinaligned filename\n .balign 16\n .incbin "\\filename"\n.endm\n\n'
assetsUCodeText = '# This file was generated by generate_ld.py\n\n'
prevAssetMadeAlignment = False
for asset in assets:
if 'lut' in asset[0]:
@@ -205,20 +206,56 @@ class LD:
if prevAssetMadeAlignment:
assetsText += '.incbinaligned "./' + asset[0] + '"\n'
else:
if asset[3]:
assetsUCodeText += '.incbin "./' + asset[0] + '"\n'
else:
assetsText += '.incbin "./' + asset[0] + '"\n'
if asset[2] == '.png' or asset[2] == '.cbin' or 'lut' in asset[0]: # Make sure that compressed data ends on a 16-byte boundary
assetsText += '.incbin "./' + asset[0] + '"\n'
if asset[1] == '.png' or asset[1] == '.cbin' or 'lut' in asset[0]: # Make sure that compressed data ends on a 16-byte boundary
#assetsText += '.balign 16\n'
prevAssetMadeAlignment = True
else:
prevAssetMadeAlignment = False
with open(ASSETS_S_FILENAME, "w") as assetsFile:
with open(ASSETS_FILENAME, "w") as assetsFile:
assetsFile.write(assetsText)
with open(ASSETS_UCODE_S_FILENAME, "w") as assetsFile:
assetsLutText = '# This file was generated by generate_ld.py\n\n'
assetsLutText += '.incbin "./' + mainLUT + '"\n'
with open(ASSETS_LUT_FILENAME, "w") as assetsLutFile:
assetsLutFile.write(assetsLutText)
def get_ucode_files(self):
ucode_files = [[], []]
assetFilenames = FileUtil.getListOfFiles(UCODE_DIR)
regex = r'[.][\/]ucode[\/][^\/]*[\/](([^\/]*[\/])?[^.]*)[.](([.]?[^.]*)+)([.][A-Za-z0-9_]*)'
for filename in assetFilenames:
matches = re.match(regex, filename)
if matches is None:
raise Exception('Invalid filename: \"' + filename + '"')
matchedGroups = matches.groups()
fileExtension = matchedGroups[4]
fileProperties = matchedGroups[2].split('.')
ramAddress = fileProperties[0]
outFilename = BUILD_DIR + '/ucode/' + matchedGroups[0] + '.' + matchedGroups[2] + '.bin'
if matchedGroups[0].startswith('ucode_'):
ucode_files[0].append((outFilename, ramAddress, fileExtension))
else:
ucode_files[1].append((outFilename, ramAddress, fileExtension))
ucode_files[0].sort(key = lambda x: x[1]) # Sort tuples by RAM address
ucode_files[1].sort(key = lambda x: x[1]) # Sort tuples by RAM address
return ucode_files
def generate_ucode_files(self):
ucodeFiles = self.get_ucode_files()
assetsUCodeText = '# This file was generated by generate_ld.py\n\n'
assetsUCodeData = '# This file was generated by generate_ld.py\n\n'
for file in ucodeFiles[0]:
assetsUCodeText += '.incbin "./' + file[0] + '"\n'
for file in ucodeFiles[1]:
assetsUCodeData += '.incbin "./' + file[0] + '"\n'
with open(UCODE_TEXT_FILENAME, "w") as assetsFile:
assetsFile.write(assetsUCodeText)
with open(UCODE_DATA_FILENAME, "w") as assetsFile:
assetsFile.write(assetsUCodeData)