Created
July 14, 2016 22:01
-
-
Save dilworks/7b3c76fbc905662fbbb38b2d5c3496ab to your computer and use it in GitHub Desktop.
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
/* | |
* 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