Added Yay0 support with auto detection for pkmn-stadium files

This commit is contained in:
KiritoDv
2024-10-11 13:54:38 -06:00
parent d13284f0ca
commit 6ccdeaa226
6 changed files with 125 additions and 46 deletions
+72
View File
@@ -0,0 +1,72 @@
#include "yay0.h"
#include "libmio0/utils.h"
#include <string.h>
#include <stdlib.h>
uint8_t* yay0_decode(const uint8_t* in_buf, uint32_t* out_size){
const uint8_t* in = in_buf;
const char* magic = (const char*) in;
if(!strncmp(magic, "PERS-SZP", 8)){
uint32_t header_size = read_u32_be(in + 8);
magic += header_size;
in += header_size;
}
if(strncmp(magic, "Yay0", 4) != 0){
return NULL;
}
uint32_t decompressed_size = read_u32_be(in + 4);
uint32_t link_table_offset = read_u32_be(in + 8);
uint32_t chunk_offset = read_u32_be(in + 12);
uint32_t link_table_idx = link_table_offset;
uint32_t chunk_idx = chunk_offset;
uint32_t other_idx = 16;
uint32_t mask_bit_counter = 0;
uint32_t current_mask = 0;
uint32_t idx = 0;
uint8_t* out = malloc(decompressed_size);
memset(out, 0, decompressed_size);
*out_size = decompressed_size;
while(idx < decompressed_size){
if(mask_bit_counter == 0){
current_mask = read_u32_be(in + other_idx);
other_idx += 4;
mask_bit_counter = 32;
}
if(current_mask & 0x80000000){
out[idx] = in[chunk_idx];
idx++;
chunk_idx++;
} else {
uint16_t link = read_u16_be(in + link_table_idx);
link_table_idx += 2;
uint32_t offset = idx - (link & 0xFFF);
uint32_t count = link >> 12;
if(count == 0){
uint8_t count_modifier = in[chunk_idx];
chunk_idx++;
count = count_modifier + 18;
} else {
count += 2;
}
for(size_t i = 0; i < count; i++){
out[idx] = out[offset + i - 1];
idx++;
}
}
current_mask <<= 1;
mask_bit_counter--;
}
return out;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
extern uint8_t* yay0_decode(const uint8_t* in, uint32_t* out_size);
+2 -25
View File
@@ -339,7 +339,7 @@ void Companion::ParseCurrentFileConfig(YAML::Node node) {
if (segments[0].IsSequence() && segments[0].size() == 2) {
gCurrentSegmentNumber = segments[0][0].as<uint32_t>();
gCurrentFileOffset = segments[0][1].as<uint32_t>();
gCurrentCompressionType = GetCompressionType(this->gRomData, gCurrentFileOffset);
gCurrentCompressionType = Decompressor::GetCompressionType(this->gRomData, gCurrentFileOffset);
if(node["no_compression"]) {
gCurrentCompressionType = CompressionType::None;
}
@@ -518,7 +518,7 @@ void Companion::ProcessFile(YAML::Node root) {
if (segments[0].IsSequence() && segments[0].size() == 2) {
gCurrentSegmentNumber = segments[0][0].as<uint32_t>();
gCurrentFileOffset = segments[0][1].as<uint32_t>();
gCurrentCompressionType = GetCompressionType(this->gRomData, gCurrentFileOffset);
gCurrentCompressionType = Decompressor::GetCompressionType(this->gRomData, gCurrentFileOffset);
if(root[":config"]["no_compression"]) {
gCurrentCompressionType = CompressionType::None;
}
@@ -1263,29 +1263,6 @@ std::optional<std::shared_ptr<BaseFactory>> Companion::GetFactory(const std::str
return this->gFactories[type];
}
/**
* @param offset Rom offset of compressed mio0 file.
* @returns CompressionType
*/
CompressionType Companion::GetCompressionType(std::vector<uint8_t>& buffer, const uint32_t offset) {
if (offset) {
LUS::BinaryReader reader((char*) buffer.data() + offset, sizeof(uint32_t));
reader.SetEndianness(Torch::Endianness::Big);
const std::string header = reader.ReadCString();
// Check if a compressed header exists
if (header == "MIO0") {
return CompressionType::MIO0;
} else if (header == "YAY0") {
return CompressionType::YAY0;
} else if (header == "YAZ0") {
return CompressionType::YAZ0;
}
}
return CompressionType::None;
}
std::optional<Table> Companion::SearchTable(uint32_t addr){
for(auto& table : this->gTables){
if(addr >= table.start && addr <= table.end){
-1
View File
@@ -147,7 +147,6 @@ public:
std::optional<std::uint32_t> GetFileOffset(void) const { return this->gCurrentFileOffset; };
std::optional<std::uint32_t> GetCurrSegmentNumber(void) const { return this->gCurrentSegmentNumber; };
CompressionType GetCurrCompressionType(void) const { return this->gCurrentCompressionType; };
CompressionType GetCompressionType(std::vector<uint8_t>& buffer, const uint32_t offset);
std::optional<VRAMEntry> GetCurrentVRAM(void) const { return this->gCurrentVram; };
std::optional<Table> SearchTable(uint32_t addr);
+45 -20
View File
@@ -6,6 +6,7 @@
extern "C" {
#include <libmio0/mio0.h>
#include <libyay0/yay0.h>
#include <libmio0/tkmk00.h>
}
@@ -31,13 +32,23 @@ DataChunk* Decompressor::Decode(const std::vector<uint8_t>& buffer, const uint32
gCachedChunks[offset] = new DataChunk{ decompressed, head.dest_size };
return gCachedChunks[offset];
}
case CompressionType::YAY0: {
uint32_t size = 0;
uint8_t* decompressed = yay0_decode(in_buf, &size);
if(!decompressed){
throw std::runtime_error("Failed to decode YAY0");
}
gCachedChunks[offset] = new DataChunk{ decompressed, size };
return gCachedChunks[offset];
}
default:
throw std::runtime_error("Unknown compression type");
}
}
DataChunk* Decompressor::DecodeTKMK00(const std::vector<uint8_t>& buffer, const uint32_t offset, const uint32_t size, const uint32_t alpha) {
if(gCachedChunks.contains(offset)){
return gCachedChunks[offset];
}
@@ -59,18 +70,13 @@ DecompressedData Decompressor::AutoDecode(YAML::Node& node, std::vector<uint8_t>
auto fileOffset = TranslateAddr(offset, true);
// Extract compressed assets.
if (node["mio0"]) {
auto assetPtr = ASSET_PTR(offset);
auto gameSize = Companion::Instance->GetRomData().size();
auto fileOffset = TranslateAddr(offset, true);
offset = ASSET_PTR(offset);
auto decoded = Decode(buffer, fileOffset + offset, CompressionType::MIO0);
if (node["mio0"] || node["yay0"]) {
auto foffset = TranslateAddr(offset, true);
auto decoded = Decode(buffer, foffset, node["mio0"] ? CompressionType::MIO0 : CompressionType::YAY0);
auto size = node["size"] ? node["size"].as<size_t>() : manualSize.value_or(decoded->size);
return {
.root = decoded,
.segment = { decoded->data, size }
.root = decoded,
.segment = { decoded->data, size }
};
}
@@ -80,8 +86,6 @@ DecompressedData Decompressor::AutoDecode(YAML::Node& node, std::vector<uint8_t>
const auto width = GetSafeNode<uint32_t>(node, "width");
const auto height = GetSafeNode<uint32_t>(node, "height");
const auto textureSize = width * height * 2;
auto assetPtr = ASSET_PTR(offset);
auto gameSize = Companion::Instance->GetRomData().size();
auto fileOffset = TranslateAddr(offset, true);
offset = ASSET_PTR(offset);
@@ -89,26 +93,24 @@ DecompressedData Decompressor::AutoDecode(YAML::Node& node, std::vector<uint8_t>
auto decoded = DecodeTKMK00(buffer, fileOffset + offset, textureSize, alpha);
auto size = node["size"] ? node["size"].as<size_t>() : manualSize.value_or(decoded->size);
return {
.root = decoded,
.segment = { decoded->data, size }
.root = decoded,
.segment = { decoded->data, size }
};
}
// Extract compressed files that contain many assets.
switch(type) {
case CompressionType::MIO0:
{
case CompressionType::YAY0:
case CompressionType::MIO0: {
offset = ASSET_PTR(offset);
auto decoded = Decode(buffer, fileOffset, CompressionType::MIO0);
auto decoded = Decode(buffer, fileOffset, type);
auto size = node["size"] ? node["size"].as<size_t>() : manualSize.value_or(decoded->size - offset);
return {
.root = decoded,
.segment = { decoded->data + offset, size }
};
}
case CompressionType::YAY0:
throw std::runtime_error("Found compressed yay0 segment.\nDecompression of yay0 has not been implemented yet.");
case CompressionType::YAZ0:
throw std::runtime_error("Found compressed yaz0 segment.\nDecompression of yaz0 has not been implemented yet.");
case CompressionType::None: // The data does not have compression
@@ -158,6 +160,29 @@ uint32_t Decompressor::TranslateAddr(uint32_t addr, bool baseAddress){
return addr;
}
CompressionType Decompressor::GetCompressionType(std::vector<uint8_t>& buffer, const uint32_t offset) {
if (offset) {
LUS::BinaryReader reader((char*) buffer.data() + offset, sizeof(uint32_t));
reader.SetEndianness(Torch::Endianness::Big);
const std::string header = reader.ReadCString();
// Check if a compressed header exists
if (header == "MIO0") {
return CompressionType::MIO0;
}
if (header == "Yay0" || header == "PERS") {
return CompressionType::YAY0;
}
if (header == "Yaz0") {
return CompressionType::YAZ0;
}
}
return CompressionType::None;
}
bool Decompressor::IsSegmented(uint32_t addr) {
if(IS_SEGMENTED(addr)){
const auto segment = Companion::Instance->GetFileOffsetFromSegmentedAddr(SEGMENT_NUMBER(addr));
+1
View File
@@ -36,6 +36,7 @@ public:
static DataChunk* DecodeTKMK00(const std::vector<uint8_t>& buffer, const uint32_t offset, const uint32_t size, const uint32_t alpha);
static DecompressedData AutoDecode(YAML::Node& node, std::vector<uint8_t>& buffer, std::optional<size_t> size = std::nullopt);
static DecompressedData AutoDecode(uint32_t offset, std::optional<size_t> size, std::vector<uint8_t>& buffer);
static CompressionType GetCompressionType(std::vector<uint8_t>& buffer, const uint32_t offset);
static uint32_t TranslateAddr(uint32_t addr, bool baseAddress = false);
static bool IsSegmented(uint32_t addr);