mirror of
https://github.com/izzy2lost/Diddy-Kong-Racing.git
synced 2026-06-19 01:16:26 -07:00
(8.08%) Overhauled assets & decompiled 5 functions in unknown_0777A0
This commit is contained in:
+36
-439
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+98
-61
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user