/*
  (c) 2008 Ender
  This utility will replace a module in Phoenix BIOS image.
*/

#include <stdlib.h>
#include <stdio.h>

char *memsearch(char *haystack, char *needle, int haystack_len, int needle_len)
{
    char *match_begin = haystack, *nd_end = needle + needle_len, *hs_end = haystack + haystack_len;
    char *hs = haystack, *nd = needle;
    while(hs != hs_end) {
       if(*nd != *hs) {
           nd = needle;
           hs = ++match_begin;
       }
       nd++; hs++;
       if(nd == nd_end) {
           return match_begin;
       }
    }
    return NULL;
}

int main(int argc, char **argv)
{
    printf("PhnxMod 0.1 2008-02-19\n(c) Ender\n\n");
    if(argc != 4) {
        printf("This utility will replace module in Phoenix BIOS image.\nUsage: \n  phnxmod bios.rom old.mod new.mod\n  phnxmod otherone.wph oldmod.mod newmod.mod\n\nModules must have 27-byte headers and if they're expected to be compressed,\nthen they have to already be. And that's it.\n");
        return 0;
    }

    int filesize, romsize, start, end, numdwords, checksum_pos = 0, i;
    unsigned long sum, old_checksum, new_checksum, tmp, extd = ('E')|('X'<<8)|('T'<<16)|('D'<<24);
    char *rom, *old_module, *new_module, *rom_module;
    FILE *fr = fopen(argv[1], "rb+"), *fom = fopen(argv[2], "rb"), *fnm = fopen(argv[3], "rb");
    int fsize, omsize, nmsize;
    
    if(!fr) {
        printf("Failed to open ROM file %s.\n", argv[1]);
        return 5;
    }
    if(!fom) {
        printf("Failed to open old module %s.\n", argv[2]);
        return 5;
    }
    if(!fnm) {
        printf("Failed to open new module %s.\n", argv[3]);
        return 5;
    }
    fseek(fr, 0, SEEK_END);
    fsize = ftell(fr);
    fseek(fom, 0, SEEK_END);
    omsize = ftell(fom);
    fseek(fnm, 0, SEEK_END);
    nmsize = ftell(fnm);

    romsize = -1;
    while(fsize != 0) {
        romsize++;
        fsize >>= 1;
    }
    
    printf("Okay, all files open.\nROM size %Xh, old module %Xh+1Bh, new module %Xh+1Bh.\nLoading data...", 1 << romsize, omsize-0x1B, nmsize-0x1B);
    
    rom = (char *) malloc(1 << romsize);
    if(!rom) { printf("Cannot allocate memory. Quitting.\n"); return 10; }
    fseek(fr, 0, SEEK_SET);
    fread(rom, 1 << romsize, 1, fr);
    
    
    old_module = (char *) malloc(omsize);
    if(!old_module) { printf("Cannot allocate memory. Quitting.\n"); return 10; }
    fseek(fom, 0, SEEK_SET);
    fread(old_module, omsize, 1, fom);

    new_module = (char *) malloc(nmsize);
    if(!new_module) { printf("Cannot allocate memory. Quitting.\n"); return 10; }
    fseek(fnm, 0, SEEK_SET);
    fread(new_module, nmsize, 1, fnm);

    fclose(fom);
    fclose(fnm);
    
    if(NULL == (rom_module = memsearch(rom, old_module + 27, 1 << romsize, omsize - 27))) {
        printf("Old module (without header) not found in the ROM file. Quitting.\n");
        return 5;
    }
    if(memsearch(rom_module + 1, old_module + 27, (1 << romsize) - (rom_module - rom) - 1, omsize - 27)) {
        printf("Old module (without header) found more than once in the ROM file. Quitting.\n");
        return 5;
    }
   
    printf("Old module (without header) found at %Xh. ", (rom_module - rom) - 27);
    if(omsize == nmsize) {
        memcpy(rom_module, new_module + 27, nmsize - 27);
        printf("Replaced. \n");
    }
    else {
        printf("New module size is different. Not implemented (yet).\n");
        return 2;
    }
    
    printf("Writing modified ROM back... ");
    fseek(fr, 0, SEEK_SET);
    fwrite(rom, 1 << romsize, 1, fr);
    fclose(fr);
    
    printf("Done.\n");
        
    printf("\n---------------- WARNING! ----------------\n");
    printf("If you don't know what you're doing there's a very high risk of rendering your\ncomputer unusable by flashing a modified BIOS. This is not a safe playground.\n");
    printf("---------------- WARNING! ----------------\n");
    
    fclose(fr);

    return 0;
}
