summaryrefslogtreecommitdiff
path: root/tools/br_ips/ips_patch.c
blob: c912474a89533e56f7cfce9919c48e978e02359f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "global.h"

static const char SPLASH[] = "Small IPS patch utility\n"
                             "Created by PikalaxALT on 23 June 2019 All Rights Reserved\n";

static const char HELP[] = "ips_patch [-h] ROM PATCH OUT\n"
                           "\n"
                           "    ROM - input ROM file\n"
                           "    PATCH - IPS patch file to apply\n"
                           "    OUT - path to write patched ROM\n"
                           "\n"
                           "Options:\n"
                           "    -h - show this message and exit\n";

int main(int argc, char ** argv) {
    // Show a friendly message
    puts(SPLASH);
    // If requested, show help message
    if (argc >= 2 && strcmp(argv[1], "-h") == 0) {
        puts(HELP);
        return 0;
    }
    // Enforce CLI syntax
    if (argc != 4) FATAL_ERROR("usage: %s [-h] ROM PATCH OUT\n", argv[0]);
    FILE * rom = fopen(argv[1], "rb");
    if (rom == NULL) FATAL_ERROR("failed to open file \"%s\" for reading\n", argv[1]);
    FILE * patch = fopen(argv[2], "rb");
    if (patch == NULL) FATAL_ERROR("failed to open file \"%s\" for reading\n", argv[2]);
    FILE * out = fopen(argv[3], "wb");
    if (patch == NULL) FATAL_ERROR("failed to open file \"%s\" for writing\n", argv[3]);
    // IPS magic header
    char magic[5];
    if (fread(magic, 1, 5, patch) != 5) FATAL_ERROR("read magic\n");
    if (memcmp(magic, "PATCH", 5) != 0) FATAL_ERROR("malformed IPS patch\n");
    // Read the ROM into allocated memory.
    fseek(rom, 0, SEEK_END);
    size_t romsize = ftell(rom);
    fseek(rom, 0, SEEK_SET);
    char * buffer = malloc(romsize);
    if (buffer == NULL) FATAL_ERROR("failed to allocate dest buffer\n");
    if (fread(buffer, 1, romsize, rom) != romsize) FATAL_ERROR("read ROM\n");
    fclose(rom);
    while (1) {
        uint32_t offset;
        size_t size;
        // Read each hunk into the buffer, overwriting previous data.
        // If two or more hunks overlap, the newest one is retained.
        // A good IPS patch creator will avoid this.
        offset = (unsigned char)getc(patch) << 16;
        offset |= (unsigned char)getc(patch) << 8;
        offset |= (unsigned char)getc(patch);
        if (offset == 0x454F46) break; // end sentinel "EOF"
        size = (unsigned char)getc(patch) << 8;
        size |= (unsigned char)getc(patch);
        if (offset + size > romsize) FATAL_ERROR("segment extends past end of ROM\n");
        if (fread(buffer + offset, 1, size, patch) != size) FATAL_ERROR("read segment\n");
    }
    fclose(patch);
    // Write the patched ROM
    fwrite(buffer, 1, romsize, out);
    fclose(out);
    free(buffer);
    return 0;
}