File:  [Generator SEGA Genesis emulator] / generator / main / patch.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed Mar 4 04:47:21 2020 UTC (6 years, 4 months ago) by root
Branches: MAIN, JamesPonder
CVS tags: v0_35, HEAD
0.35

/* Generator is (c) James Ponder, 1997-2001 http://www.squish.net/generator/ */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include "generator.h"

#include "ui.h"
#include "cpu68k.h"
#include "patch.h"

/* Merlyn LeRoy's lamp.c comment: */

/*
 * "Eet ees not *I* who am crazy, eet ees I who am MAD!" - R. Ho"ek
 *
 * lamp.c - a program to control Game Genie for Genesis
 *
 * Translate between Game Genie for Genesis 8-character codes and
 * the 24-bit address & 16-bit value for that address.
 * The format is this:
 *
 * ijklm nopIJ KLMNO PABCD EFGHd efgha bcQRS TUVWX   decodes to...
 *
 * ABCDEFGH IJKLMNOP QRSTUVWX: abcdefgh ijklmnop
 * 24-bit address              16-bit data
 * MSB                    LSB  MSB           LSB
 *
 * ..where each group of five letters is a Genie code character
 * (ABCDEFGHJKLMNPRSTVWXYZ0123456789, each representing 5 bits 00000-11111),
 * and each 8-character Genie code is the 24-bit address and 16-bit data.
 *
 * For example, SCRA-BJX0 is a game genie code.  Each letter is 5 bits from
 * the table ABCDEFGHJKLMNPRSTVWXYZ0123456789, A=00000, B=00001, C=00010...
 *
 *   S     C     R     A  -  B     J     X     0
 * 01111 00010 01110 00000 00001 01000 10011 10110
 * ijklm nopIJ KLMNO PABCD EFGHd efgha bcQRS TUVWX   rearrange as...
 *
 * 00000000 10011100 01110110: 01010100 01111000
 * ABCDEFGH IJKLMNOP QRSTUVWX: abcdefgh ijklmnop
 * 24-bit address              16-bit data
 * MSB                    LSB  MSB           LSB
 *
 * Which is 009c76: 5478
 *
 * See the usage message when run with no arguments...
 * Merlyn LeRoy (Brian Westley), [email protected] 1/4/93
 */

static const char *patch_codestring = "ABCDEFGHJKLMNPRSTVWXYZ0123456789";

/*** externed ***/

t_patchlist *patch_patchlist; /* patches */

/*** patch_savefile - save current patches to file ***/

int patch_savefile(const char *filename)
{
  FILE *f;
  t_patchlist *ent;

  if ((f = fopen(filename, "wb")) == NULL) {
    LOG_CRITICAL(("Failed to open '%s' for writing: %s",
                  filename, strerror(errno)));
    return -1;
  }
  for (ent = patch_patchlist; ent; ent = ent->next)
    fprintf(f, "%s    %s\r\n", ent->code, ent->action);
  if (fclose(f) == EOF) {
    LOG_CRITICAL(("Failed to close '%s': %s", filename, strerror(errno)));
    return -1;
  }
  return 0;
}

/*** patch_loadfile - load the patch file ***/

int patch_loadfile(const char *filename)
{
  char linebuf[256];
  char *p;
  FILE *f;
  t_patchlist *ent, **end;

  if ((f = fopen(filename, "rb")) == NULL) {
    LOG_CRITICAL(("Failed to open '%s': %s", filename, strerror(errno)));
    return -1;
  }
  patch_clearlist();
  end = &patch_patchlist;
  while (fgets(linebuf, sizeof(linebuf), f)) {
    if (linebuf[strlen(linebuf)-1] != '\n') {
      LOG_CRITICAL(("Line too long in '%s': %s", filename, strerror(errno)));
      return -1;
    }
    linebuf[strlen(linebuf)-1] = '\0'; /* remove newline */
    if (*linebuf && linebuf[strlen(linebuf)-1] == '\r')
      linebuf[strlen(linebuf)-1] = '\0'; /* remove optional cr */
    if (strlen(linebuf) < 11 && linebuf[6] != ':') {
      LOG_CRITICAL(("Invalid patch file '%s'", filename));
      return -1;
    }
    if ((ent = malloc(sizeof(t_patchlist))) == NULL)
      ui_err("out of memory");
    snprintf(ent->code, 12, "%s", linebuf);
    for (p = linebuf + 11; *p == ' '; p++) ;
    if ((ent->action = strdup(p)) == NULL)
      ui_err("out of memory");
    ent->next = NULL;
    *end = ent;
    end = &ent->next;
  }
  if (!feof(f)) {
    LOG_CRITICAL(("Error whilst reading patch file '%s'", filename));
    return -1;
  }
  if (fclose(f) == EOF) {
    LOG_CRITICAL(("Failed to close '%s': %s", filename, strerror(errno)));
    return -1;
  }
  return 0;
}

void patch_clearlist(void)
{
  t_patchlist *ent;

  /* clear out old patch list */
  while (patch_patchlist) {
    ent = patch_patchlist;
    patch_patchlist = patch_patchlist->next;
    free(ent->action);
    free(ent);
  }
}

void patch_addcode(const char *code, const char *action)
{
  t_patchlist *ent, **end;

  /* where's the end of the list */
  for (end = &patch_patchlist; *end; end = &((*end)->next)) ;

  /* create and insert */
  if ((ent = malloc(sizeof(t_patchlist))) == NULL)
    ui_err("out of memory");
  snprintf(ent->code, sizeof(ent->code), "%s", code);
  ent->action = strdup(action);
  ent->next = NULL;
  *end = ent;
}

int patch_apply(const char *code, const char *action)
{
  uint32 addr;
  uint32 data;
  char *end;

  (void)action;
  addr = strtol(code, &end, 16);
  if (*end++ != ':')
    return -1;
  data = strtol(end, &end, 16);
  if (*end)
    return -1;
  if (addr & ~0xfffffe || data & ~0xffff)
    return -1;
  if (addr < cpu68k_romlen) {
    printf("applied %X = %X\n", addr, data);
    ((uint16 *)cpu68k_rom)[addr >> 1] = LOCENDIAN16((uint16)data);
    gen_modifiedrom = 1;
  } else if ((addr & 0xe00000) == 0xe00000) {
    printf("applied %X = %X\n", addr, data);
    ((uint16 *)cpu68k_ram)[(addr & 0xfff) >> 1] = LOCENDIAN16((uint16)data);
  } else {
    return -1;
  }
  return 0;
}

/* given a game genie code, convert to 24-bit address, 16-bit data */

int patch_genietoraw(const char *code, uint32 *addr, uint16 *data)
{
  char *p;
  uint32 a, d, v;
  int i;

  if (strlen(code) != 9 || code[4] != '-')
    return -1;
  for (i = 0; i < 9; i = (i == 3 ? i+2 : i+1)) {
    if ((p = strchr(patch_codestring, code[i])) == NULL)
      return -1;
    v = p - patch_codestring;
    switch (i) {
    case 0: d|= v << 3; break;
    case 1: d|= v >> 2; a|= (v & 3) << 14; break;
    case 2: a|= v << 9; break;
    case 3: a|= (v >> 4) << 8; a|= (v & 15) << 20; break;
    case 5: a|= (v >> 1) << 16; d|= (v & 1) << 12; break;
    case 6: d|= (v >> 1) << 8; d|= (v & 1) << 15; break;
    case 7: d|= (v >> 3) << 13; a|= (v & 7) << 5; break;
    case 8: a|= v; break; 
    default: return -1;
    }
  }
  *addr = a;
  *data = d;
  return 0;
}

/* given a 24-bit address, 16-bit data, create game genie code */

int patch_rawtogenie(uint32 addr, uint16 data, char *code)
{
  uint8 v;
  int i;

  if (addr & 0xff000000)
    return -1;
  code[4] = '-';
  for (i = 0; i < 9; i = (i == 3 ? i+2 : i+1)) {
    switch (i) {
    case 0: v = (data >> 3) & 15; break;
    case 1: v = ((data & 7) << 2) | ((addr >> 14) & 3); break;
    case 2: v = (addr >> 9) & 15; break;
    case 3: v = (addr >> 20) | ((addr >> 4) & 0x10); break;
    case 5: v = ((addr >> 15) & 14) | ((data >> 12) & 1); break;
    case 6: v = ((data >> 8) & 14) | (data >> 15); break;
    case 7: v = ((data >> 10) & 0x18) | ((addr >> 5) & 7); break;
    case 8: v = addr & 15; break;
    }
    code[i] = patch_codestring[v];
  }
  return 0;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.