mirror of
https://github.com/GearsProgress/Poke_Transporter_GB.git
synced 2026-03-21 17:34:42 -05:00
214 lines
8.2 KiB
C++
214 lines
8.2 KiB
C++
#include "gb_rom_values/gb_rom_values.h"
|
|
#include "gba_rom_values/gba_rom_values.h"
|
|
#include "payloads/payload_file_writer.h"
|
|
#include "payloads/payload_file_reader.h"
|
|
#include "payloads/binary_patch_generator.h"
|
|
#include "payloads/payload_builder.h"
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
/**
|
|
* Generates the payloads for the specific pokémon generation.
|
|
* Note: yellow_version indicates that we'd want to generate the payloads for pokémon yellow.
|
|
* The reason we single out pokémon yellow, is because the binary patch diff is significant compared to Blue, Red and Green.
|
|
* It's easier to compress the data if we generate it into a separate file.
|
|
*/
|
|
void generate_payloads_for(uint8_t generation, bool yellow_version, const char *full_path)
|
|
{
|
|
uint8_t base_payload_buffer[PAYLOAD_SIZE];
|
|
uint8_t other_payload_buffer[PAYLOAD_SIZE];
|
|
payload_file_writer payload_writer;
|
|
binary_patch_generator patch_generator;
|
|
u16 rom_set_index;
|
|
u16 base_payload_index;
|
|
u16 index;
|
|
|
|
const struct GB_ROM *rom_value_sets[] = {
|
|
gb_rom_values_eng,
|
|
gb_rom_values_fre,
|
|
gb_rom_values_ger,
|
|
gb_rom_values_ita,
|
|
gb_rom_values_spa,
|
|
};
|
|
|
|
const u16 rom_value_sizes[] = {
|
|
gb_rom_values_eng_size,
|
|
gb_rom_values_fre_size,
|
|
gb_rom_values_ger_size,
|
|
gb_rom_values_ita_size,
|
|
gb_rom_values_spa_size,
|
|
};
|
|
|
|
const u8 num_elements = sizeof(rom_value_sizes) / sizeof(u16);
|
|
|
|
// search for the first english GB_ROM struct for the given generation
|
|
for (base_payload_index = 0; base_payload_index < gb_rom_values_eng_size; ++base_payload_index)
|
|
{
|
|
if (gb_rom_values_eng[base_payload_index].generation == generation)
|
|
{
|
|
if ((!yellow_version && gb_rom_values_eng[base_payload_index].version != YELLOW_ID) || (yellow_version && gb_rom_values_eng[base_payload_index].version == YELLOW_ID))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
memset(base_payload_buffer, 0, sizeof(base_payload_buffer));
|
|
|
|
// initialize the found GB_ROM struct as the base payload
|
|
init_payload(base_payload_buffer, gb_rom_values_eng[base_payload_index], TRANSFER, false);
|
|
payload_writer.set_base_payload(gb_rom_values_eng[base_payload_index].language, gb_rom_values_eng[base_payload_index].version, base_payload_buffer, gb_rom_values_eng[base_payload_index].payload_size);
|
|
|
|
for (rom_set_index = 0; rom_set_index < num_elements; ++rom_set_index)
|
|
{
|
|
for (index = 0; index < rom_value_sizes[rom_set_index]; ++index)
|
|
{
|
|
const struct GB_ROM *curr_rom = &rom_value_sets[rom_set_index][index];
|
|
if (curr_rom->generation != generation ||
|
|
(rom_set_index == 0 && index == base_payload_index) ||
|
|
(yellow_version && curr_rom->version != YELLOW_ID) ||
|
|
(!yellow_version && curr_rom->version == YELLOW_ID))
|
|
{
|
|
// skip if:
|
|
// - the generation does not match
|
|
// - if it's the base payload
|
|
// - if we specified yellow_version == true and the current rom is not a pokémon yellow rom.
|
|
// - if we specified yellow_version == false and the current rom IS a pokémon yellow rom.
|
|
continue;
|
|
}
|
|
|
|
// add the binary patches for this ROM
|
|
memset(other_payload_buffer, 0, PAYLOAD_SIZE);
|
|
init_payload(other_payload_buffer, *curr_rom, TRANSFER, false);
|
|
binary_patch_list patches = patch_generator.diff(base_payload_buffer, other_payload_buffer, PAYLOAD_SIZE);
|
|
payload_writer.add_binary_patches(curr_rom->language, curr_rom->version, patches);
|
|
}
|
|
}
|
|
|
|
payload_writer.write_to_file(full_path);
|
|
}
|
|
|
|
void test_payloads(const char *full_path)
|
|
{
|
|
uint8_t buffer[4096]; // 4096 bytes is enough for the payloads
|
|
uint8_t reference_payload_buffer[PAYLOAD_SIZE];
|
|
uint8_t reconstructed_payload_buffer[PAYLOAD_SIZE];
|
|
FILE *file;
|
|
size_t size;
|
|
u16 rom_set_index;
|
|
u16 index;
|
|
|
|
file = fopen(full_path, "rb"); /*open file*/
|
|
fseek(file, 0, SEEK_END);
|
|
size = ftell(file); /*calc the size needed*/
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
payload_file_reader payload_reader(buffer, size);
|
|
|
|
const struct GB_ROM *rom_value_sets[] = {
|
|
gb_rom_values_eng,
|
|
gb_rom_values_fre,
|
|
gb_rom_values_ger,
|
|
gb_rom_values_ita,
|
|
gb_rom_values_spa,
|
|
};
|
|
|
|
const u16 rom_value_sizes[] = {
|
|
gb_rom_values_eng_size,
|
|
gb_rom_values_fre_size,
|
|
gb_rom_values_ger_size,
|
|
gb_rom_values_ita_size,
|
|
gb_rom_values_spa_size,
|
|
};
|
|
|
|
const u8 num_elements = sizeof(rom_value_sizes) / sizeof(u16);
|
|
|
|
fread(&buffer, 1, size, file);
|
|
|
|
for (rom_set_index = 0; rom_set_index < num_elements; ++rom_set_index)
|
|
{
|
|
for (index = 0; index < rom_value_sizes[rom_set_index]; ++index)
|
|
{
|
|
const struct GB_ROM *curr_rom = &rom_value_sets[rom_set_index][index];
|
|
|
|
// first generate the reference payload
|
|
memset(reference_payload_buffer, 0, PAYLOAD_SIZE);
|
|
init_payload(reference_payload_buffer, *curr_rom, TRANSFER, false);
|
|
|
|
// now read the payload from the file
|
|
memset(reconstructed_payload_buffer, 0, PAYLOAD_SIZE);
|
|
|
|
// okay, so, the given file may or may not contain the desired payload.
|
|
// we should just skip if the read call returns false
|
|
if (!payload_reader.read_payload(reconstructed_payload_buffer, curr_rom->language, curr_rom->version))
|
|
{
|
|
continue; // skip if the payload was not found
|
|
}
|
|
|
|
// printf("Testing payload from file %s for language %u, variant %u: ", full_path, curr_rom->language, curr_rom->version);
|
|
if (!memcmp(reference_payload_buffer, reconstructed_payload_buffer, PAYLOAD_SIZE))
|
|
{
|
|
printf("P");
|
|
}
|
|
else
|
|
{
|
|
printf("\n\n!!!Payload from file %s for language %u, variant %u failed the payload test!!!\n", full_path, curr_rom->language, curr_rom->version);
|
|
// print the differences
|
|
for (size_t i = 0; i < PAYLOAD_SIZE; ++i)
|
|
{
|
|
if (reference_payload_buffer[i] != reconstructed_payload_buffer[i])
|
|
{
|
|
printf("Byte %zu: expected 0x%02X, got 0x%02X\n", i, reference_payload_buffer[i], reconstructed_payload_buffer[i]);
|
|
}
|
|
}
|
|
abort(); // stop execution on failure
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void generate_and_test_payloads_for(uint8_t generation, bool yellow_version, const char *output_path, const char *filename)
|
|
{
|
|
|
|
char full_output_path[4096];
|
|
|
|
if (output_path[0] != '\0')
|
|
{
|
|
snprintf(full_output_path, sizeof(full_output_path), "%s/%s", output_path, filename);
|
|
}
|
|
else
|
|
{
|
|
strncpy(full_output_path, filename, sizeof(full_output_path));
|
|
}
|
|
|
|
if (std::filesystem::exists(full_output_path))
|
|
{
|
|
|
|
std::filesystem::file_time_type inf_time = std::filesystem::last_write_time("tools/payload-generator/src/payloads/payload_builder.cpp");
|
|
std::filesystem::file_time_type outf_time = std::filesystem::last_write_time(full_output_path);
|
|
|
|
if (outf_time > inf_time)
|
|
{
|
|
// printf("File %s is newer than %s, skipping\n", full_output_path, input_path);
|
|
printf("S");
|
|
return;
|
|
}
|
|
}
|
|
generate_payloads_for(generation, yellow_version, full_output_path);
|
|
test_payloads(full_output_path);
|
|
printf("B");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
printf("Converting data into bin files!\n[");
|
|
const char *output_path = (argc > 1) ? argv[1] : "";
|
|
|
|
generate_and_test_payloads_for(1, false, output_path, "gb_gen1_payloads_RB.bin");
|
|
generate_and_test_payloads_for(1, true, output_path, "gb_gen1_payloads_Y.bin");
|
|
generate_and_test_payloads_for(2, false, output_path, "gb_gen2_payloads.bin");
|
|
|
|
printf("]\nConvertion finished!\n\n");
|
|
return 0;
|
|
} |