Last active
September 12, 2015 06:20
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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