Skip to content

Instantly share code, notes, and snippets.

@dilworks
Created July 14, 2016 22:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dilworks/7b3c76fbc905662fbbb38b2d5c3496ab to your computer and use it in GitHub Desktop.
Save dilworks/7b3c76fbc905662fbbb38b2d5c3496ab to your computer and use it in GitHub Desktop.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package ve.net.tsdx.gs.mdrom;
import java.nio.charset.Charset;
import java.util.*;
import ve.net.tsdx.gs.*;
import ve.net.tsdx.gs.locale.*;
/** Cabecera de ROM de Genesis
*
* @author tomman
*/
public class SEGAROMHeader {
/** Encabezado en bruto */
private byte[] rawHeader;
/** Tabla de excepciones 68K: 0x0000 (256 bytes, 64 offsets) */
private EnumMap<M68KInterrupts, Integer> interrupts;
/** Nombre de la consola: 0x0100 (16 bytes) */
private String consoleName;
/** Copyright y fecha: 0x0110 (16 bytes) */
private String copyrightDate;
/** Nombre domestico (Japón) del juego: 0x0120 (48 bytes) */
private String domesticName;
/** Nombre internacional del juego: 0x0150 (48 bytes) */
private String overseasName;
/** Tipo de programa, serial y versión: 0x0180 (14 bytes) */
private String psvString;
/** Checksum de la ROM: 0x018E (2 bytes) */
private short checksum;
/** Dispositivos de E/S soportados: 0x0190 (16 bytes) */
private String ioDevices;
/** Inicio de la ROM: 0x01A0 (4 bytes) */
private int romStart;
/** Fin de la ROM: 0x01A4 (4 bytes) */
private int romEnd;
/** Inicio de la RAM: 0x01A8 (4 bytes) */
private int ramStart;
/** Fin de la RAM: 0x01AC (4 bytes) */
private int ramEnd;
/** ID de la RAM externa: 0x01B0 (4 bytes) */
private int eRAMID;
/** Inicio de la RAM externa: 0x01B4 (4 bytes) */
private int eRAMStart;
/** Fin de la RAM externa: 0x01B8 (4 bytes) */
private int eRAMEnd;
/** Datos del modem: 0x01BC (12 bytes) */
private String modemData;
/** Notas/memo: 0x01C8 (40 bytes) */
private String memo;
/** Regiones: 0x01F0 (16 bytes) */
private String countryCodes;
private Utils u;
private Localizer loc;
/** Tabla de interrupciones 68K */
public enum M68KInterrupts {
/** Apuntador inicial de la pila */
InitialStackPointer,
/** Punto de entrada del programa */
ProgramStart,
/** Error de bus */
BusError,
/** Error de dirección */
AddressError,
/** Instrucción ilegal */
IllegalInstruction,
/** División por cero */
DivisionByZero,
/** Excepción CHK */
CHKException,
/** Excepción TRAPV */
TRAPVException,
/** Violación de privilegio */
PrivilegeViolation,
/** Excepción TRACE */
TRACEException,
/** Emulador Line-A */
LineAEmulator,
/** Emulador Line-F */
LineFEmulator,
/** Reservada */
Reserved00,
/** Reservada */
Reserved01,
/** Reservada */
Reserved02,
/** Reservada */
Reserved03,
/** Reservada */
Reserved04,
/** Reservada */
Reserved05,
/** Reservada */
Reserved06,
/** Reservada */
Reserved07,
/** Reservada */
Reserved08,
/** Reservada */
Reserved09,
/** Reservada */
Reserved10,
/** Reservada */
Reserved11,
/** Excepción inesperada */
SpuriousException,
/** IRQ nivel 1 */
IRQLevel1,
/** IRQ nivel 2 */
IRQLevel2,
/** IRQ nivel 3 */
IRQLevel3,
/** IRQ nivel 4 (H-Blank) */
IRQLevel4,
/** IRQ nivel 5 */
IRQLevel5,
/** IRQ nivel 6 (V-Blank) */
IRQLevel6,
/** IRQ nivel 7 */
IRQLevel7,
/** Excepción TRAP #00 */
TRAP00,
/** Excepción TRAP #01 */
TRAP01,
/** Excepción TRAP #02 */
TRAP02,
/** Excepción TRAP #03 */
TRAP03,
/** Excepción TRAP #04 */
TRAP04,
/** Excepción TRAP #05 */
TRAP05,
/** Excepción TRAP #06 */
TRAP06,
/** Excepción TRAP #07 */
TRAP07,
/** Excepción TRAP #08 */
TRAP08,
/** Excepción TRAP #09 */
TRAP09,
/** Excepción TRAP #10 */
TRAP10,
/** Excepción TRAP #11 */
TRAP11,
/** Excepción TRAP #12 */
TRAP12,
/** Excepción TRAP #13 */
TRAP13,
/** Excepción TRAP #14 */
TRAP14,
/** Excepción TRAP #15 */
TRAP15,
/** Reservada */
Reserved12,
/** Reservada */
Reserved13,
/** Reservada */
Reserved14,
/** Reservada */
Reserved15,
/** Reservada */
Reserved16,
/** Reservada */
Reserved17,
/** Reservada */
Reserved18,
/** Reservada */
Reserved19,
/** Reservada */
Reserved20,
/** Reservada */
Reserved21,
/** Reservada */
Reserved22,
/** Reservada */
Reserved23,
/** Reservada */
Reserved24,
/** Reservada */
Reserved25,
/** Reservada */
Reserved26,
/** Reservada */
Reserved27;
}
/** Dispositivos de E/S soportados */
public enum IODevices {
// FIXME: Aca hay dispositivos que no salieron al mercado, y otros tantos de la Master System
// Algunos de stos son identificadores oficiales de Sega (vease sega2.doc)
// Confirmar y depurar lista...
/** Control estandar de 3 botones */
DEV_Gamepad('J', "devGamepad"),
/** Control de 6 botones */
DEV_6BTN_Gamepad('6', "dev6BTNGamepad"),
/** Teclado (BR/JP) */
DEV_Keyboard('K', "devKeyboard"),
/** Impresora (???) */
DEV_Printer('P', "devPrinter"),
/** Control Ball (???) */
DEV_ControlBall('B', "devControlBall"),
/** Disquetera (nunca salió al mercado) */
DEV_Floppy('F', "devFloppy"),
/** Activator */
DEV_Activator('L', "devActivator"),
/** Adaptador multijugador de Sega */
DEV_TeamPlay('4', "devTeamPlay"),
/** Control estandar de Master System */
DEV_MSJoystick('0', "devMSJoystick"),
/** Puerto serial RS232C */
DEV_RS232C('R', "devRS232C"),
/** Tableta de dibujo (nunca salió al mercado) */
DEV_Tablet('T', "devTablet"),
/** Control de pala para Master System */
DEV_Paddle('V', "devPaddle"),
/** Soporte para CD-ROM */
DEV_CDROM('C', "devCDROM"),
/** Ratón */
DEV_Mouse('M', "devMouse");
/** ID del dispositivo */
private final char devID;
/** Descripción del dispositivo */
private final String devName;
/** Constructor */
private IODevices(char devID, String devName) {
this.devID = devID;
this.devName = devName;
}
/** Retorna el ID de este dispositivo de E/S
*
* @return Caracter identificador del dispositivo
*/
public char getDeviceID() {
return this.devID;
}
/** Retorna el nombre localizado de este dispositivo de E/S
*
* @return Nombre comercial del dispositivo de E/S
*/
public String getDeviceName() {
return Localizer.getInstance().getMessage(this.devName);
}
}
/** Regiones para juegos de SEGA */
public enum SEGARegions {
/** Japón, Taiwan, Corea del Sur, y otros territorios NTSC en Asia */
Zone_NTSC_Japan('J', (byte) 1, "zoneNTSCJapan"),
/** China, Malasia, Tailandia, y otros territorios PAL en Asia */
Zone_PAL_Japan('J', (byte) 2, "zonePALJapan"),
/** Estados Unidos, Brasil, el resto de la América, y demas territorios NTSC */
Zone_NTSC_Overseas('U', (byte) 4, "zoneNTSCOverseas"),
/** Europa, Hong Kong, y demas territorios PAL */
Zone_PAL_Overseas('E', (byte) 8, "zonePALOverseas");
/** Identificador de zona (modelo antiguo, antes de 1994, un caracter por zona) */
private final char oldZoneID;
/** Identificador de zona (modelo nuevo, a partir de 1994, máscara de bits) */
private final byte newZoneID;
/** Nombre de la zona */
private final String zoneName;
/** Constructor */
private SEGARegions(char oldZoneID, byte newZoneID, String zoneName) {
this.oldZoneID = oldZoneID;
this.newZoneID = newZoneID;
this.zoneName = zoneName;
}
/** Retorna el identificador de la zona según el antiguo modelo de regiones
* de Sega (anterior a 1994, letra identificadora de región)
*
* @return Letra identificadora de la zona
*/
public char getOldZoneID() {
return this.oldZoneID;
}
/** Retorna el identificador de la zona según el nuevo modelo de regiones de Sega
* (a partir de 1994, bit identificador de región)
*
* @return Bit identificador de la zona
*/
public byte getNewZoneID() {
return this.newZoneID;
}
/** Extrae el identificador de la zona según el nuevo modelo de regiones de Sega
* (a partir de 1994, máscara de bits identificadora de regiónes)
*
* @param zone Máscara de regiones permitidas para el programa
* @return Bit de región permitidas para este programa
*/
public byte getNewZoneID(byte zone) {
return (byte) (this.newZoneID & zone);
}
/** Retorna el nombre localizado de la zona
*
* @return Nombre de la zona (probablemente según Sega)
*/
public String getZoneName() {
return Localizer.getInstance().getMessage(this.zoneName);
}
}
/** Constructor */
public SEGAROMHeader() {
this.u = Utils.getInstance();
this.loc = Localizer.getInstance();
}
// TODO: Comprobar que todos estos setters/getters hagan lo que deben hacer
/** Retorna el nombre de la consola
* @return Nombre de la consola
*/
public String getConsoleName() {
return this.consoleName;
}
/** Establece el nombre de la consola
* @param consoleName Nombre de la consola, máximo 16 bytes
*/
public void setConsoleName(String consoleName) {
this.consoleName = this.u.padWithSpaces(consoleName, 16);
}
/** Retorna la linea de copyright y fecha de la ROM
* @return Linea de copyright y fecha de la ROM
*/
public String getCopyrightDate() {
return this.copyrightDate;
}
/** Establece la linea de copyright y fecha de la ROM
* @param copyrightDate Linea de copyright y fecha de la rom, máximo 16 caracteres
*/
public void setCopyrightDate(String copyrightDate) {
this.copyrightDate = this.u.padWithSpaces(copyrightDate, 16);
}
/** Retorna el nombre doméstico (en Japón) de la ROM
* @return Nombre doméstico (en Japón) de la ROM
*/
public String getDomesticName() {
return this.domesticName;
}
/** Establece el nombre doméstico (en Japón) de la ROM
* @param domesticName Nombre doméstico (en Japón) de la ROM, máximo 48 bytes
*/
public void setDomesticName(String domesticName) {
this.domesticName = this.u.padWithSpaces(domesticName, 48);
}
/** Retorna el nombre internacional de la ROM
* @return Nombre internacional de la ROM
*/
public String getOverseasName() {
return this.overseasName;
}
/** Establece el nombre internacional de la ROM
* @param overseasName Nombre internacional de la ROM, máximo 48 bytes
*/
public void setOverseasName(String overseasName) {
this.overseasName = this.u.padWithSpaces(overseasName, 48);
}
/** Retorna el tipo, serial y versión del programa
* @return Tipo, serial y versión del programa
*/
public String getPsvString() {
return this.psvString;
}
/** Establece el tipo, serial y versión del programa
* @param psvString Tipo, serial y versión del programa, máximo 14 bytes
*/
public void setPsvString(String psvString) {
this.psvString = this.u.padWithSpaces(psvString, 14);
}
/** Retorna el checksum de la ROM
* @return Checksum de la ROM
*/
public short getChecksum() {
return this.checksum;
}
/** Establece el checksum de la ROM
* @param checksum Checksum de la ROM, 2 bytes
*/
public void setChecksum(short checksum) {
this.checksum = checksum;
}
/** Retorna el mapa de dispositivos de E/S soportados
* @return Mapa de dispositivos de E/S soportados
*/
public String getIoDevices() {
return this.ioDevices;
}
/** Establece el mapa de dispositivos de E/S soportados
* @param ioDevices Mapa de dispositivos de E/S soportados, máximo 16 bytes
*/
public void setIoDevices(String ioDevices) {
this.ioDevices = this.u.padWithSpaces(ioDevices, 16);
}
/** Retorna la dirección del comienzo de la ROM
* @return Dirección donde comienza la ROM
*/
public int getRomStart() {
return this.romStart;
}
/** Establece la dirección del comienzo de la ROM
* @param romStart Dirección donde comienza la ROM (típicamente 0)
*/
public void setROMStart(int romStart) {
this.romStart = romStart;
}
/** Retorna la dirección del final de la ROM
* @return Dirección donde termina la ROM
*/
public int getROMEnd() {
return this.romEnd;
}
/** Establece la dirección del final de la ROM
* @param romEnd Dirección donde termina la ROM
*/
public void setROMEnd(int romEnd) {
this.romEnd = romEnd;
}
/** Retorna la dirección del comienzo de la RAM
* @return Dirección donde comienza la RAM
*/
public int getRAMStart() {
return this.ramStart;
}
/** Establece la dirección del comienzo de la RAM
* @param ramStart Dirección donde comienza la RAM (típicamente 0xFF0000)
*/
public void setRAMStart(int ramStart) {
this.ramStart = ramStart;
}
/** Retorna la dirección del final de la RAM
* @return Dirección donde termina la RAM
*/
public int getRAMEnd() {
return this.ramEnd;
}
/** Establece la dirección del final de la RAM
* @param ramEnd Dirección donde termina la RAM (típicamente 0xFFFFFF)
*/
public void setRAMEnd(int ramEnd) {
this.ramEnd = ramEnd;
}
/** Retorna el identificador de la RAM externa
* @return Identificador de la RAM externa
*/
public int getERAMID() {
return this.eRAMID;
}
/** Establece el identificador de la RAM externa
* @param eRAMID Identificador de la RAM externa
* (0x20202020 si el programa no usa RAM adicional en el cartucho)
*/
public void seteRAMID(int eRAMID) {
this.eRAMID = eRAMID;
}
/** Retorna la dirección del inicio de la RAM externa
* @return Dirección donde comienza la RAM externa
*/
public int getERAMStart() {
return this.eRAMStart;
}
/** Establece la dirección del inicio de la RAM externa
* @param sRAMStart Dirección donde comienza la RAM externa
* (0x20202020 si el programa no usa este tipo de RAM)
*/
public void setERAMStart(int eRAMStart) {
this.eRAMStart = eRAMStart;
}
/** Retorna la dirección del final de la RAM externa
* @return Dirección donde termina la RAM externa
*/
public int getERAMEnd() {
return this.eRAMEnd;
}
/** Establece la dirección del inicio de la RAM externa
* @param sRAMEnd Dirección donde termina la RAM externa
* (0x20202020 si el programa no usa este tipo de RAM)
*/
public void setERAMEnd(int eRAMEnd) {
this.eRAMEnd = eRAMEnd;
}
/** Retorna la información del soporte para modem
* @return Identificador del modem
*/
public String getModemData() {
return this.modemData;
}
/** Establece la información del soporte para modem
* @param modemData Identificador del modem, máximo 12 bytes
*/
public void setModemData(String modemData) {
this.modemData = this.u.padWithSpaces(modemData, 12);
}
/** Retorna la información adicional sobre la ROM (notas, etc.)
* @return Información adicional de la ROM
*/
public String getMemo() {
return this.memo;
}
/** Establece la información adicional sobre la ROM (notas, etc.)
* @param memo Información adicional de la ROM, máximo 40 bytes
*/
public void setMemo(String memo) {
this.memo = this.u.padWithSpaces(memo, 40);
}
/** Retorna las regiones para las cuales esta ROM esta autorizada
* @return Mapa de regiones autorizadas para ejecutar esta ROM
*/
public String getCountryCodes() {
return countryCodes;
}
/** Establece las regiones para las cuales esta ROM está autorizada
* @param countryCodes Mapa de regiones autorizadas para ejecutar esta ROM
*/
public void setCountryCodes(String countryCodes) {
this.countryCodes = countryCodes;
}
/** Establece las regiones para las cuales esta ROM está autorizada
*
* @param zone Zona a añadir a la configuración de regiones
* @param newModel Define si se usa el nuevo modelo de zonas de Sega (a partir de 1994)
* @param keepZones Solo añadir zona, no reemplazar las ya existentes
*/
public void setCountryCodes(SEGARegions zone, boolean newModel, boolean keepZones) {
if (newModel) {
// Usemos el nuevo modelo
try {
if (keepZones) {
// Añadir una nueva zona al mapa
byte curZone = Integer.decode("0x" + this.countryCodes.trim()).byteValue();
this.countryCodes = Byte.toString(zone.getNewZoneID(curZone));
} else {
// Reemplazar las zonas que tenemos aca
this.countryCodes = Byte.toString(zone.getNewZoneID());
}
} catch (NumberFormatException nfe) {
if (keepZones) {
// Como que estamos usando el modelo viejo aca, no?
throw new IllegalArgumentException(this.loc.getMessage("headerZoneModelMismatch"));
}
}
} else {
// Usemos el viejo modelo
if (keepZones) {
if (!this.countryCodes.contains(Character.toString(zone.getOldZoneID()))) {
// No tenemos la zona en la lista. Vamos a ponerla al final
this.countryCodes = this.countryCodes.trim() +
Character.toString(zone.getOldZoneID());
}
} else {
this.countryCodes = Character.toString(zone.getOldZoneID());
}
}
}
/** Retorna la dirección de un manejador de interrupción del encabezado actual
*
* @param intName Vector de interrupción 68K
* @return Direccion del manejador de interrupción
*/
public int getInterrupt(M68KInterrupts intName) {
return this.interrupts.get(intName);
}
/** Establece la dirección de un manejador de interrupción del encabezado actual
*
* @param intName Vector de interrupción 68K
* @param offset Direccion del manejador de interrupción
*/
public void setInterrupt(M68KInterrupts intName, int offset) {
this.interrupts.put(intName, offset);
}
/** Carga el encabezado de una ROM
*
* @param rawHeader
*/
public void loadHeader(byte[] rawHeader) {
if (rawHeader.length < 512) {
// Este encabezado esta incompleto
throw new IllegalArgumentException(this.loc.getMessage("headerErrTooSmall",
rawHeader.length));
}
// Guardemos una copia del encabezado en bruto
this.rawHeader = rawHeader;
// Poblemos los campos
// Construimos primero la tabla de interrupciones
this.interrupts = new EnumMap<>(M68KInterrupts.class);
int offset = 0;
byte hdrOffset[] = new byte[4];
for (M68KInterrupts interrupt : M68KInterrupts.values()) {
System.arraycopy(rawHeader, offset, hdrOffset, 0, 4);
this.interrupts.put(interrupt, this.u.byteArrayToInt(hdrOffset));
offset += 4;
}
//GSConsole.message("Interrupt table: " + this.interrupts);
// Ahora vamos con el encabezado en si
// Nombre de la consola: 0x0100 (16 bytes)
this.consoleName = new String(Arrays.copyOfRange(rawHeader, 0x0100, 0x0110));
// Copyright y fecha: 0x0110 (16 bytes)
this.copyrightDate = new String(Arrays.copyOfRange(rawHeader, 0x0110, 0x0120));
// Nombre domestico (Japón) del juego: 0x0120 (48 bytes)
// NOTA: Esto lo decodificamos como Shift-JIS, dado que es el nombre japones!
this.domesticName = new String(Arrays.copyOfRange(rawHeader, 0x0120, 0x0150),
Charset.forName("SJIS"));
// Nombre internacional del juego: 0x0150 (48 bytes)
this.overseasName = new String(Arrays.copyOfRange(rawHeader, 0x0150, 0x0180));
// Tipo de programa, serial y versión: 0x0180 (14 bytes)
this.psvString = new String(Arrays.copyOfRange(rawHeader, 0x0180, 0x018E));
// Checksum de la ROM: 0x018E (2 bytes)
this.checksum = this.u.byteArrayToShort(Arrays.copyOfRange(rawHeader, 0x018E, 0x0190));
// Dispositivos de E/S soportados: 0x0190 (16 bytes)
this.ioDevices = new String(Arrays.copyOfRange(rawHeader, 0x0190, 0x01A0));
// Inicio de la ROM: 0x01A0 (4 bytes)
this.romStart = this.u.byteArrayToInt(Arrays.copyOfRange(rawHeader, 0x01A0, 0x01A4));
// Fin de la ROM: 0x01A4 (4 bytes)
this.romEnd = this.u.byteArrayToInt(Arrays.copyOfRange(rawHeader, 0x01A4, 0x01A8));
// Inicio de la RAM: 0x01A8 (4 bytes)
this.ramStart = this.u.byteArrayToInt(Arrays.copyOfRange(rawHeader, 0x01A8, 0x01AC));
// Fin de la RAM: 0x01AC (4 bytes)
this.ramEnd = this.u.byteArrayToInt(Arrays.copyOfRange(rawHeader, 0x01AC, 0x01B0));
// ID de la RAM externa: 0x01B0 (4 bytes)
this.eRAMID = this.u.byteArrayToInt(Arrays.copyOfRange(rawHeader, 0x01B0, 0x01B4));
// Inicio de la RAM externa: 0x01B4 (4 bytes)
this.eRAMStart = this.u.byteArrayToInt(Arrays.copyOfRange(rawHeader, 0x01B4, 0x01B8));
// Fin de la RAM externa: 0x01B8 (4 bytes)
this.eRAMEnd = this.u.byteArrayToInt(Arrays.copyOfRange(rawHeader, 0x01B8, 0x01BC));
// Datos del modem: 0x01BC (12 bytes)
this.modemData = new String(Arrays.copyOfRange(rawHeader, 0x01BC, 0x01C8));
// Notas/memo: 0x01C8 (40 bytes)
this.memo = new String(Arrays.copyOfRange(rawHeader, 0x01C8, 0x01F0));
// Regiones: 0x01F0 (16 bytes)
this.countryCodes = new String(Arrays.copyOfRange(rawHeader, 0x01F0, 0x0200));
//GSConsole.message("Header parsed for '" + this.getOverseasName() + "'");
}
/** Actualiza y regenera el encabezado de la ROM
*
* @return Nuevo encabezado para la ROM
*/
public byte[] rebuildHeader() {
if (this.rawHeader == null) {
throw new IllegalStateException(this.loc.getMessage("headerErrNoHeader"));
}
// Recreamos el encabezado
byte newHdr[] = new byte[512];
int offset = 0;
// Comencemos poniendo la tabla de excepciones
for (M68KInterrupts interrupt : M68KInterrupts.values()) {
byte intrOffset[] = this.u.longToByteArray(this.interrupts.get(interrupt));
System.arraycopy(intrOffset, 0, newHdr, offset, 4);
offset += 4;
}
// Procedamos ahora con los demas campos
// Nombre de la consola: 0x0100 (16 bytes)
offset = 0x0100;
System.arraycopy(this.u.stringToASCIIArray(this.consoleName), 0, newHdr, offset, 16);
// Copyright y fecha: 0x0110 (16 bytes)
offset = 0x0110;
System.arraycopy(this.u.stringToASCIIArray(this.copyrightDate), 0, newHdr, offset, 16);
// Nombre domestico (Japón) del juego: 0x0120 (48 bytes)
offset = 0x0120;
System.arraycopy(this.u.stringToSJISArray(this.domesticName), 0, newHdr, offset, 48);
// Nombre internacional del juego: 0x0150 (48 bytes)
offset = 0x0150;
System.arraycopy(this.u.stringToASCIIArray(this.overseasName), 0, newHdr, offset, 48);
// Tipo de programa, serial y versión: 0x0180 (14 bytes)
offset = 0x0180;
System.arraycopy(this.u.stringToASCIIArray(this.psvString), 0, newHdr, offset, 14);
// Checksum de la ROM: 0x018E (2 bytes)
offset = 0x018E;
System.arraycopy(this.u.shortToByteArray(this.checksum), 0, newHdr, offset, 2);
// Dispositivos de E/S soportados: 0x0190 (16 bytes)
offset = 0x0190;
System.arraycopy(this.u.stringToASCIIArray(this.ioDevices), 0, newHdr, offset, 16);
// Inicio de la ROM: 0x01A0 (4 bytes)
offset = 0x01A0;
System.arraycopy(this.u.longToByteArray(this.romStart), 0, newHdr, offset, 4);
// Fin de la ROM: 0x01A4 (4 bytes)
offset = 0x01A4;
System.arraycopy(this.u.longToByteArray(this.romEnd), 0, newHdr, offset, 4);
// Inicio de la RAM: 0x01A8 (4 bytes)
offset = 0x01A8;
System.arraycopy(this.u.longToByteArray(this.ramStart), 0, newHdr, offset, 4);
// Fin de la RAM: 0x01AC (4 bytes)
offset = 0x01AC;
System.arraycopy(this.u.longToByteArray(this.ramEnd), 0, newHdr, offset, 4);
// ID de la RAM externa: 0x01B0 (4 bytes)
offset = 0x01B0;
System.arraycopy(this.u.longToByteArray(this.eRAMID), 0, newHdr, offset, 4);
// Inicio de la RAM externa: 0x01B4 (4 bytes)
offset = 0x01B4;
System.arraycopy(this.u.longToByteArray(this.eRAMStart), 0, newHdr, offset, 4);
// Fin de la RAM externa: 0x01B8 (4 bytes)
offset = 0x01B8;
System.arraycopy(this.u.longToByteArray(this.eRAMEnd), 0, newHdr, offset, 4);
// Datos del modem: 0x01BC (12 bytes)
offset = 0x01BC;
System.arraycopy(this.u.stringToASCIIArray(this.modemData), 0, newHdr, offset, 12);
// Notas/memo: 0x01C8 (40 bytes)
offset = 0x01C8;
System.arraycopy(this.u.stringToASCIIArray(this.memo), 0, newHdr, offset, 40);
// Regiones: 0x01F0 (16 bytes)
offset = 0x01F0;
System.arraycopy(this.u.stringToASCIIArray(this.countryCodes), 0, newHdr, offset, 16);
return newHdr;
}
/** Formatea y retorna la tabla de interrupciones de la ROM
*
* @return Tabla de vectores de interrupciónes 68K formateada
*/
public String interruptTableInfo() {
if (this.rawHeader == null) {
return this.loc.getMessage("headerNoData");
}
StringBuilder ret = new StringBuilder("");
ret.append(this.loc.getMessage("header68KInterruptTable")).append("\n");
ret.append(this.u.repeatSingleChar('=', ret.length() - 1)).append("\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("header68KInterrupt"), 24));
ret.append(this.loc.getMessage("header68KOffset")).append("\n");
for (M68KInterrupts interrupt : M68KInterrupts.values()) {
ret.append(this.u.padWithSpaces(interrupt.name(), 24));
ret.append("0x").append(String.format("%08X", this.interrupts.get(interrupt)));
ret.append("\n");
}
return ret.toString();
}
/** Formatea y retorna la información del encabezado de la ROM
*
* @return Información del encabezado de la ROM
*/
public String headerInfo() {
if (this.rawHeader == null) {
return this.loc.getMessage("headerNoData");
}
int i;
boolean idFound;
StringBuilder ret = new StringBuilder("");
ret.append(this.loc.getMessage("headerROMHeaderInfo")).append("\n");
ret.append(this.u.repeatSingleChar('=', ret.length() - 1)).append("\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerConsoleName"), 32)).append("'")
.append(this.consoleName).append("'\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerCopyright"), 32)).append("'")
.append(this.copyrightDate).append("'\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerDomesticName"), 32)).append("'")
.append(this.domesticName).append("'\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerOverseasName"), 32)).append("'")
.append(this.overseasName).append("'\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerSerialVersion"), 32)).append("'")
.append(this.psvString).append("'\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerChecksum"), 32)).append("0x")
.append(String.format("%04X", this.checksum)).append("\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerIODevices"), 32)).append("'")
.append(this.ioDevices).append("'\n");
// Identificar los dispositivos que tenemos aca
for (i = 0; i < this.ioDevices.length(); i++) {
if (this.ioDevices.charAt(i) == ' ' || this.ioDevices.charAt(i) == 0) {
// Hemos llegado al final de la lista
break;
}
idFound = false;
for (IODevices dev : IODevices.values()) {
if (this.ioDevices.charAt(i) == dev.getDeviceID()) {
idFound = true;
ret.append("- ").append(this.loc.getMessage("headerDevice",
dev.getDeviceName())).append("\n");
break;
}
}
if (!idFound) {
// Dispositivo desconocido?
ret.append("- ").append(this.loc.getMessage("headerUnknownDevice",
this.ioDevices.charAt(i))).append("\n");
}
}
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerROMStart"), 32)).append("0x")
.append(String.format("%08X", this.romStart)).append("\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerROMEnd"), 32)).append("0x")
.append(String.format("%08X", this.romEnd)).append("\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerRAMStart"), 32)).append("0x")
.append(String.format("%08X", this.ramStart)).append("\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerRAMEnd"), 32)).append("0x")
.append(String.format("%08X", this.ramEnd)).append("\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerExtRAMID"), 32));
if (this.eRAMID != 0x20202020) {
// Tenemos RAM extra en ese cartuchito: SRAM, EEPROM, mas RAM clasica, lo que sea...
ret.append("0x").append(String.format("%08X", this.eRAMID)).append("\n");
/* Decodifiquemos esa cadena
* Bytes 0-1: 'RA' (0x52 0x41, fijo)
* Byte 3: 1x1yz000
* x: 1 = Respaldo (con batería?), 0 = no respaldo (RAM pura y dura?)
* yz: 00 = Direcciones par e impar
* 01 = <ilegal>
* 10 = Solo direcciones pares
* 11 = Solo direcciones impares
* Byte 4: 00100000 (0x20, fijo)
* Por alguna razon NBA Jam usa 0x40 aca...
*/
// Validemos el ID
byte bsRAMID[] = this.u.longToByteArray(this.eRAMID);
if (bsRAMID[0] == 'R' && bsRAMID[1] == 'A') {
// ID valido de acuerdo a Sega
// Tipo de RAM?
ret.append("- ").append(this.loc.getMessage("headerExtBackupRAM",
this.loc.getMessage((bsRAMID[2] & 0x40) == 0x40 ?
"yes" : "no"))).append("\n");
ret.append("- ").append(this.loc.getMessage("headerExtRAMAddrMode",
this.loc.getMessage((bsRAMID[2] & 0x18) == 0x18 ? "headerExtRAMAddrOdd" :
((bsRAMID[2] & 0x10) == 0x10 ? "headerExtRAMAddrEven" :
"headerExtRAMAddrBoth")))).append("\n");
} else {
ret.append("- ").append(this.loc.getMessage("headerExtRAMIDInvalid")).append("\n");
}
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerExtRAMStart"), 32)).append("0x")
.append(String.format("%08X", this.eRAMStart)).append("\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerExtRAMEnd"), 32)).append("0x")
.append(String.format("%08X", this.eRAMEnd)).append("\n");
} else {
// Esta ROM declara NO tener RAM extra en el cartucho
ret.append(this.loc.getMessage("headerNoExtRAM")).append("\n");
}
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerModemData"), 32)).append("'")
.append(this.modemData).append("'\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerMemo"), 32)).append("'")
.append(this.memo).append("'\n");
ret.append(this.u.padWithSpaces(this.loc.getMessage("headerCountryCodes"), 32)).append("'")
.append(this.countryCodes).append("'\n");
// Identificar las regiones que tenemos aca
for (i = 0; i < this.countryCodes.length(); i++) {
if (this.countryCodes.charAt(i) == ' ' || this.countryCodes.charAt(i) == 0) {
// Hemos llegado al final de la lista
break;
}
idFound = false;
for (SEGARegions zone : SEGARegions.values()) {
// Intentemos usar el viejo modelo de zonas de Sega
if (this.countryCodes.charAt(i) == zone.getOldZoneID()) {
idFound = true;
ret.append("- ").append(this.loc.getMessage("headerCountry",
zone.getZoneName())).append("\n");
break;
}
}
if (!idFound) {
// No resulto con el modelo viejo. Probemos el modelo nuevo
for (SEGARegions zone : SEGARegions.values()) {
try {
byte zoneMap = Integer.decode("0x" + this.countryCodes.charAt(i)).byteValue();
if (zone.getNewZoneID(zoneMap) != 0) {
// Sip, es una zona de las nuevas
idFound = true;
ret.append("- ").append(this.loc.getMessage("headerCountry", "[1994] " +
zone.getZoneName())).append("\n");
}
} catch (NumberFormatException nfe) {
// Esto no es un mapa de bits de zonas!
}
}
if (!idFound) {
// Ahora si nos rendimos, esta zona no encaja en ninguna parte...
ret.append("- ").append(this.loc.getMessage("headerUnknownCountry",
this.countryCodes.charAt(i))).append("\n");
}
}
}
return ret.toString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment