Skip to content

Instantly share code, notes, and snippets.

@kode54
Last active September 12, 2015 06:20
Show Gist options
  • Save kode54/072e25c83f86cef6016d to your computer and use it in GitHub Desktop.
Save kode54/072e25c83f86cef6016d to your computer and use it in GitHub Desktop.
A simple patcher to fix broken PSF1 sets. In this case, it is to fix PSFs from CaitSith2's Chocobo's Dungeon 2 rip.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <stdint.h>
static uLongf decomp_size = 0;
static uint8_t exe_buffer[2 * 1024 * 1024];
static uint8_t file_buffer[3 * 1024 * 1024];
static uint32_t get_le32(const void *p)
{
return ((const uint8_t*)p)[0] | ((const uint8_t*)p)[1]<<8 | ((const uint8_t*)p)[2]<<16 | ((const uint8_t*)p)[3]<<24;
}
static void set_le32(void *p, uint32_t v)
{
((uint8_t*)p)[0] = v & 0xFF;
((uint8_t*)p)[1] = (v >> 8) & 0xFF;
((uint8_t*)p)[2] = (v >> 16) & 0xFF;
((uint8_t*)p)[3] = (v >> 24) & 0xFF;
}
void exe_unpack(const uint8_t *exe, size_t exe_size)
{
decomp_size = sizeof(exe_buffer);
int uerr = uncompress(exe_buffer, &decomp_size, exe, exe_size);
if (uerr != Z_OK)
{
decomp_size = 0;
fprintf(stderr, "Decompression failed.\n");
}
}
void exe_pack(uint8_t **exe, size_t * exe_size)
{
uLongf compress_length = decomp_size + 50 + (decomp_size / 100);
*exe = malloc(compress_length);
int cerr = compress2(*exe, &compress_length, exe_buffer, decomp_size, 9);
if (cerr != Z_OK)
{
*exe_size = 0;
fprintf(stderr, "Re-compression failed.\n");
}
*exe_size = compress_length;
}
void exe_patch()
{
uint32_t offset = get_le32(&exe_buffer[0x18]);
uint32_t plength = get_le32(&exe_buffer[0x1C]);
offset &= 0x3FFFFFFF;
if (offset <= 0xBC090 &&
(offset+plength) >= 0xBC093)
{
uint32_t patch_offset = 0xBC090 - offset + 2048;
const uint32_t patch_size = 4 * 3;
if (get_le32(&exe_buffer[patch_offset]) == 0x0802F040)
{
set_le32(&exe_buffer[patch_offset], 0);
set_le32(&exe_buffer[patch_offset+4], 0x0802F040);
set_le32(&exe_buffer[patch_offset+8], 0);
if (plength < patch_offset + patch_size)
plength = patch_offset + patch_size - 2048;
set_le32(&exe_buffer[0x1C], plength);
if (decomp_size < plength + 2048)
decomp_size = plength + 2048;
fprintf(stderr, "Patch applied. ");
return;
}
else
fprintf(stderr, "File doesn't match.\n");
}
else
fprintf(stderr, "File doesn't cover patch memory target.\n");
decomp_size = 0;
}
int main(int argc, char ** argv)
{
int i;
for (i = 1; i < argc; i++)
{
fprintf(stderr, "%s: ", argv[i]);
FILE *f = fopen(argv[i], "rb");
if (!f)
{
fprintf(stderr, "Unable to open.\n");
return 1;
}
if (fseek(f, 0, SEEK_END) < 0)
{
fclose(f);
fprintf(stderr, "Seek failed.\n");
return 1;
}
size_t file_size = ftell(f);
if (fseek(f, 0, SEEK_SET) < 0)
{
fclose(f);
fprintf(stderr, "Second seek failed.\n");
return 1;
}
if (fread(file_buffer, 1, file_size, f) != file_size)
{
fclose(f);
fprintf(stderr, "Read failed.\n");
return 1;
}
fclose(f);
if (get_le32(&file_buffer[0]) != 0x01465350)
{
fprintf(stderr, "Not a valid PSF1 file.\n");
return 1;
}
uint32_t reserved_size = get_le32(&file_buffer[4]);
uint32_t exe_compressed_size = get_le32(&file_buffer[8]);
uint32_t exe_crc32 = get_le32(&file_buffer[12]);
if (exe_crc32 != crc32(crc32(0L, Z_NULL, 0), file_buffer + 16, exe_compressed_size))
{
fprintf(stderr, "Invalid EXE section CRC32.\n");
return 1;
}
uint32_t tag_size = file_size - (16 + exe_compressed_size + reserved_size);
exe_unpack(file_buffer + 16, exe_compressed_size);
if (!decomp_size)
{
return 1;
}
exe_patch();
if (!decomp_size)
{
return 1;
}
uint8_t *repacked_exe = 0;
size_t repacked_exe_size = 0;
exe_pack(&repacked_exe, &repacked_exe_size);
if (!repacked_exe_size)
{
free(repacked_exe);
return 1;
}
uint32_t new_exe_crc32 = crc32(crc32(0L, Z_NULL, 0), repacked_exe, repacked_exe_size);
set_le32(&file_buffer[8], repacked_exe_size);
set_le32(&file_buffer[12], new_exe_crc32);
memmove(file_buffer + repacked_exe_size, file_buffer + exe_compressed_size, reserved_size + tag_size);
memcpy(file_buffer + 16, repacked_exe, repacked_exe_size);
free(repacked_exe);
size_t new_file_size = 16 + repacked_exe_size + reserved_size + tag_size;
f = fopen(argv[i], "wb");
if (!f)
{
fprintf(stderr, "Reopen for write failed.\n");
return 1;
}
if (fwrite(file_buffer, 1, new_file_size, f) != new_file_size)
{
fclose(f);
fprintf(stderr, "Write failed.\n");
return 1;
}
fclose(f);
fprintf(stderr, "File rewritten.\n");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment