mirror of
https://github.com/marcrobledo/RomPatcher.js.git
synced 2025-06-27 16:25:54 +00:00
added support for APS (GBA), fixing #58
This commit is contained in:
parent
0769fae972
commit
1ea536ad27
9 changed files with 147 additions and 133 deletions
|
@ -5,7 +5,7 @@ A ROM patcher made in HTML5.
|
|||
* Supported formats:
|
||||
* IPS
|
||||
* UPS
|
||||
* APS
|
||||
* APS (N64/GBA)
|
||||
* BPS
|
||||
* RUP
|
||||
* PPF
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
*/
|
||||
|
||||
var PRECACHE_ID='rom-patcher-js';
|
||||
var PRECACHE_VERSION='v27b';
|
||||
var PRECACHE_VERSION='v28';
|
||||
var PRECACHE_URLS=[
|
||||
'/RomPatcher.js/','/RomPatcher.js/index.html',
|
||||
'/RomPatcher.js/manifest.json',
|
||||
|
@ -41,7 +41,8 @@ var PRECACHE_URLS=[
|
|||
'/RomPatcher.js/js/zip.js/inflate.js',
|
||||
'/RomPatcher.js/js/formats/ips.js',
|
||||
'/RomPatcher.js/js/formats/ups.js',
|
||||
'/RomPatcher.js/js/formats/aps.js',
|
||||
'/RomPatcher.js/js/formats/aps_n64.js',
|
||||
'/RomPatcher.js/js/formats/aps_gba.js',
|
||||
'/RomPatcher.js/js/formats/bps.js',
|
||||
'/RomPatcher.js/js/formats/rup.js',
|
||||
'/RomPatcher.js/js/formats/ppf.js',
|
||||
|
|
|
@ -40,7 +40,8 @@
|
|||
<script type="text/javascript" src="./js/formats/zip.js"></script>
|
||||
<script type="text/javascript" src="./js/formats/ips.js"></script>
|
||||
<script type="text/javascript" src="./js/formats/ups.js"></script>
|
||||
<script type="text/javascript" src="./js/formats/aps.js"></script>
|
||||
<script type="text/javascript" src="./js/formats/aps_n64.js"></script>
|
||||
<script type="text/javascript" src="./js/formats/aps_gba.js"></script>
|
||||
<script type="text/javascript" src="./js/formats/bps.js"></script>
|
||||
<script type="text/javascript" src="./js/formats/rup.js"></script>
|
||||
<script type="text/javascript" src="./js/formats/ppf.js"></script>
|
||||
|
@ -179,7 +180,7 @@
|
|||
<button id="button-settings" class="button-outer"><img src="style/icon_settings.svg" class="icon settings" /> <span data-localize="settings">Settings</span></button>
|
||||
</div>
|
||||
|
||||
Rom Patcher JS <small>v2.7.1</small> by <a href="/">Marc Robledo</a>
|
||||
Rom Patcher JS <small>v2.8</small> by <a href="/">Marc Robledo</a>
|
||||
<br />
|
||||
<img src="style/icon_github.svg" class="icon github" /> <a href="https://github.com/marcrobledo/RomPatcher.js/" target="_blank">See on GitHub</a>
|
||||
<img src="style/icon_heart.svg" class="icon heart" /> <a href="https://www.paypal.me/marcrobledo/5" target="_blank" rel="nofollow">Donate</a>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Rom Patcher JS v20230202 - Marc Robledo 2016-2023 - http://www.marcrobledo.com/license */
|
||||
/* Rom Patcher JS v20230331 - Marc Robledo 2016-2023 - http://www.marcrobledo.com/license */
|
||||
|
||||
const TOO_BIG_ROM_SIZE=67108863;
|
||||
const HEADERS_INFO=[
|
||||
|
@ -663,8 +663,10 @@ function _readPatchFile(){
|
|||
patch=parseIPSFile(patchFile);
|
||||
}else if(header.startsWith(UPS_MAGIC)){
|
||||
patch=parseUPSFile(patchFile);
|
||||
}else if(header.startsWith(APS_MAGIC)){
|
||||
}else if(header.startsWith(APS_N64_MAGIC)){
|
||||
patch=parseAPSFile(patchFile);
|
||||
}else if(header.startsWith(APS_GBA_MAGIC)){
|
||||
patch=APSGBA.fromFile(patchFile);
|
||||
}else if(header.startsWith(BPS_MAGIC)){
|
||||
patch=parseBPSFile(patchFile);
|
||||
}else if(header.startsWith(RUP_MAGIC)){
|
||||
|
|
133
js/crc.js
133
js/crc.js
|
@ -108,126 +108,19 @@ function adler32(marcFile, offset, len){
|
|||
|
||||
|
||||
|
||||
/* CRC16 */
|
||||
/*
|
||||
const CRC16_TABLE=(function(){
|
||||
var c,crcTable=[];
|
||||
for(var n=0;n<256;n++){
|
||||
c=n;
|
||||
for(var k=0;k<8;k++)
|
||||
c=((c&1)?(0x8408^(c>>>1)):(c>>>1));
|
||||
crcTable[n]=c;
|
||||
/* CRC16/CCITT-FALSE */
|
||||
function crc16(marcFile, offset, len){
|
||||
var crc=0xffff;
|
||||
|
||||
offset=offset? offset : 0;
|
||||
len=len && len>0? len : marcFile.fileSize;
|
||||
|
||||
for(var i=0; i<len; i++){
|
||||
crc ^= marcFile._u8array[offset++] << 8;
|
||||
for (j=0; j<8; ++j) {
|
||||
crc = (crc & 0x8000) >>> 0 ? (crc << 1) ^ 0x1021 : crc << 1;
|
||||
}
|
||||
}
|
||||
return crcTable;
|
||||
}());
|
||||
function crc16(marcFile){
|
||||
var crc=0^(-1);
|
||||
|
||||
for(var i=0;i<marcFile._u8array.length;i++)
|
||||
crc=((crc>>>8)&0x0ff)^CRC16_TABLE[(crc^marcFile._u8array[i])&0xff];
|
||||
|
||||
return ((crc^(-1))>>>0) & 0xffff;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* specific ROM checksums */
|
||||
/* this is unused code, might be used in a future so ROM checksums can be fixed after patching */
|
||||
const CONSOLES=[
|
||||
{
|
||||
title:'Sega Mega Drive/Genesis',
|
||||
MEGADRIVE_LOGO:[0x53, 0x45, 0x47, 0x41, 0x20, 0x4d, 0x45, 0x47, 0x41, 0x20, 0x44, 0x52],
|
||||
GENESIS_LOGO:[0x53, 0x45, 0x47, 0x41, 0x20, 0x47, 0x45, 0x4e, 0x45, 0x53, 0x49, 0x53],
|
||||
checkHeader:function(marcFile){
|
||||
var megadrive=true;
|
||||
var genesis=true;
|
||||
for(var i=0; i<12 && (megadrive || genesis); i++){
|
||||
if(marcFile._u8array[0x100+i]!==this.MEGADRIVE_LOGO[i])
|
||||
megadrive=false;
|
||||
if(marcFile._u8array[0x100+i]!==this.GENESIS_LOGO[i])
|
||||
genesis=false;
|
||||
}
|
||||
return megadrive || genesis;
|
||||
},
|
||||
getChecksum:function(marcFile){
|
||||
return (marcFile._u8array[0x018e]<<8) + marcFile._u8array[0x018f];
|
||||
},
|
||||
recalculateChecksum:function(marcFile){
|
||||
var checksum=0;
|
||||
|
||||
for(var i=0x200; i<marcFile.fileSize; i+=2)
|
||||
checksum=(checksum + (((marcFile._u8array[i]<<8) + marcFile._u8array[i+1])>>>0)) & 0xffff;
|
||||
|
||||
return checksum
|
||||
},
|
||||
updateChecksum:function(marcFile, newChecksum){
|
||||
marcFile._u8array[0x18e]=newChecksum>>8;
|
||||
marcFile._u8array[0x18f]=newChecksum & 0xff;
|
||||
}
|
||||
},{
|
||||
title:'Game Boy',
|
||||
NINTENDO_LOGO:[0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b, 0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d],
|
||||
checkHeader:function(marcFile){
|
||||
for(var i=0; i<this.NINTENDO_LOGO.length; i++){
|
||||
if(marcFile._u8array[0x104+i]!==this.NINTENDO_LOGO[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
getChecksum:function(marcFile){
|
||||
return marcFile._u8array[0x14d]
|
||||
},
|
||||
recalculateChecksum:function(marcFile){
|
||||
var checksum=0;
|
||||
|
||||
for(var i=0x134; i<0x014d; i++){
|
||||
checksum=(checksum - marcFile._u8array[i] - 1) & 0xff;
|
||||
}
|
||||
|
||||
return checksum
|
||||
},
|
||||
updateChecksum:function(marcFile, newChecksum){
|
||||
marcFile._u8array[0x014d]=newChecksum;
|
||||
|
||||
|
||||
/* global checksum isn't checked by real hw, but fix it anyway */
|
||||
var globalChecksumOld=(marcFile._u8array[0x014e]<<8) + marcFile._u8array[0x014f];
|
||||
var globalChecksumNew=0;
|
||||
for(var i=0x0000; i<0x014e; i++)
|
||||
globalChecksumNew=((globalChecksumNew + marcFile._u8array[i]) >>> 0) & 0xffff;
|
||||
for(i=0x0150;i<marcFile.fileSize; i++)
|
||||
globalChecksumNew=((globalChecksumNew + marcFile._u8array[i]) >>> 0) & 0xffff;
|
||||
if(globalChecksumOld!==globalChecksumNew){
|
||||
marcFile._u8array[0x014e]=globalChecksumNew>>8;
|
||||
marcFile._u8array[0x014f]=globalChecksumNew & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
function checkConsole(marcFile){
|
||||
return false;
|
||||
}
|
||||
function fixConsoleChecksum(marcFile){
|
||||
var system=false;
|
||||
for(var i=0; i<CONSOLES.length && !system; i++)
|
||||
if(CONSOLES[i].checkHeader(marcFile))
|
||||
system=CONSOLES[i];
|
||||
if(!system)
|
||||
return false;
|
||||
|
||||
var oldChecksum=console.getChecksum(marcFile);
|
||||
var newChecksum=console.recalculateChecksum(marcFile);
|
||||
|
||||
if(oldChecksum!==newChecksum)
|
||||
if(alert('Fix '+console.title+' checksum?')){
|
||||
console.updateChecksum(marcFile, newChecksum);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return crc & 0xffff;
|
||||
}
|
114
js/formats/aps_gba.js
Normal file
114
js/formats/aps_gba.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
/* APS (GBA) module for Rom Patcher JS v20230331 - Marc Robledo 2017-2023 - http://www.marcrobledo.com/license */
|
||||
/* File format specification: https://github.com/btimofeev/UniPatcher/wiki/APS-(GBA) */
|
||||
|
||||
const APS_GBA_MAGIC='APS1';
|
||||
const APS_GBA_BLOCK_SIZE=0x010000; //64Kb
|
||||
const APS_GBA_RECORD_SIZE=4 + 2 + 2 + APS_GBA_BLOCK_SIZE;
|
||||
|
||||
function APSGBA(){
|
||||
this.sourceSize=0;
|
||||
this.targetSize=0;
|
||||
this.records=[];
|
||||
}
|
||||
APSGBA.prototype.addRecord=function(offset, sourceCrc16, targetCrc16, xorBytes){
|
||||
this.records.push({
|
||||
offset:offset,
|
||||
sourceCrc16:sourceCrc16,
|
||||
targetCrc16:targetCrc16,
|
||||
xorBytes:xorBytes}
|
||||
);
|
||||
}
|
||||
APSGBA.prototype.toString=function(){
|
||||
var s='Total records: '+this.records.length;
|
||||
s+='\nInput file size: '+this.sourceSize;
|
||||
s+='\nOutput file size: '+this.targetSize;
|
||||
return s
|
||||
}
|
||||
APSGBA.prototype.validateSource=function(sourceFile){
|
||||
if(sourceFile.fileSize!==this.sourceSize)
|
||||
return false;
|
||||
|
||||
var originalEndianness=sourceFile.littleEndian;
|
||||
sourceFile.littleEndian=true;
|
||||
for(var i=0; i<this.records.length; i++){
|
||||
sourceFile.seek(this.records[i].offset);
|
||||
var bytes=sourceFile.readBytes(APS_GBA_BLOCK_SIZE);
|
||||
if(crc16(sourceFile, this.records[i].offset, APS_GBA_BLOCK_SIZE) !== this.records[i].sourceCrc16)
|
||||
return false
|
||||
}
|
||||
sourceFile.littleEndian=originalEndianness;
|
||||
|
||||
return true
|
||||
}
|
||||
APSGBA.prototype.export=function(fileName){
|
||||
var patchFileSize=12 + (this.records.length * APS_GBA_RECORD_SIZE);
|
||||
|
||||
tempFile=new MarcFile(patchFileSize);
|
||||
tempFile.littleEndian=true;
|
||||
tempFile.fileName=fileName+'.aps';
|
||||
tempFile.writeString(APS_GBA_MAGIC, APS_GBA_MAGIC.length);
|
||||
tempFile.writeU32(this.sourceSize);
|
||||
tempFile.writeU32(this.targetSize);
|
||||
|
||||
for(var i=0; i<this.records.length; i++){
|
||||
tempFile.writeU32(this.records[i].offset);
|
||||
tempFile.writeU16(this.records[i].sourceCrc16);
|
||||
tempFile.writeU16(this.records[i].targetCrc16);
|
||||
tempFile.writeBytes(this.records[i].xorBytes);
|
||||
}
|
||||
|
||||
return tempFile
|
||||
}
|
||||
|
||||
APSGBA.prototype.apply=function(romFile, validate){
|
||||
if(validate && !this.validateSource(romFile)){
|
||||
throw new Error('error_crc_input');
|
||||
}
|
||||
|
||||
tempFile=new MarcFile(this.targetSize);
|
||||
romFile.copyToFile(tempFile, 0, romFile.fileSize);
|
||||
|
||||
for(var i=0; i<this.records.length; i++){
|
||||
romFile.seek(this.records[i].offset);
|
||||
tempFile.seek(this.records[i].offset);
|
||||
for(var j=0; j<APS_GBA_RECORD_SIZE; j++){
|
||||
tempFile.writeU8(romFile.readU8() ^ this.records[i].xorBytes[j]);
|
||||
}
|
||||
|
||||
if(validate && crc16(tempFile, this.records[i].offset, APS_GBA_BLOCK_SIZE)!==this.records[i].targetCrc16){
|
||||
throw new Error('error_crc_output');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return tempFile
|
||||
}
|
||||
|
||||
|
||||
APSGBA.fromFile=function(patchFile){
|
||||
patchFile.seek(0);
|
||||
patchFile.littleEndian=true;
|
||||
|
||||
if(
|
||||
patchFile.readString(APS_GBA_MAGIC.length)!==APS_GBA_MAGIC ||
|
||||
patchFile.fileSize < (12 + APS_GBA_RECORD_SIZE) ||
|
||||
(patchFile.fileSize-12)%APS_GBA_RECORD_SIZE!==0
|
||||
)
|
||||
return null;
|
||||
|
||||
var patch=new APSGBA();
|
||||
|
||||
patch.sourceSize=patchFile.readU32();
|
||||
patch.targetSize=patchFile.readU32();
|
||||
|
||||
while(!patchFile.isEOF()){
|
||||
var offset=patchFile.readU32();
|
||||
var sourceCrc16=patchFile.readU16();
|
||||
var targetCrc16=patchFile.readU16();
|
||||
var xorBytes=patchFile.readBytes(APS_GBA_BLOCK_SIZE);
|
||||
|
||||
patch.addRecord(offset, sourceCrc16, targetCrc16, xorBytes);
|
||||
}
|
||||
return patch;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/* APS (N64) module for Rom Patcher JS v20180930 - Marc Robledo 2017-2018 - http://www.marcrobledo.com/license */
|
||||
/* File format specification: https://github.com/btimofeev/UniPatcher/wiki/APS-(N64) */
|
||||
|
||||
const APS_MAGIC='APS10';
|
||||
const APS_N64_MAGIC='APS10';
|
||||
const APS_RECORD_RLE=0x0000;
|
||||
const APS_RECORD_SIMPLE=0x01;
|
||||
const APS_N64_MODE=0x01;
|
||||
|
@ -61,7 +61,7 @@ APS.prototype.export=function(fileName){
|
|||
tempFile=new MarcFile(patchFileSize);
|
||||
tempFile.littleEndian=true;
|
||||
tempFile.fileName=fileName+'.aps';
|
||||
tempFile.writeString(APS_MAGIC, APS_MAGIC.length);
|
||||
tempFile.writeString(APS_N64_MAGIC, APS_N64_MAGIC.length);
|
||||
tempFile.writeU8(this.headerType);
|
||||
tempFile.writeU8(this.encodingMethod);
|
||||
tempFile.writeString(this.description, 50);
|
|
@ -1,10 +1,11 @@
|
|||
/* Rom Patcher JS v20200502 - Marc Robledo 2016-2020 - http://www.marcrobledo.com/license */
|
||||
/* Rom Patcher JS v20230331 - Marc Robledo 2016-2023 - http://www.marcrobledo.com/license */
|
||||
|
||||
self.importScripts(
|
||||
'./MarcFile.js',
|
||||
'./crc.js',
|
||||
'./formats/ips.js',
|
||||
'./formats/aps.js',
|
||||
'./formats/aps_n64.js',
|
||||
'./formats/aps_gba.js',
|
||||
'./formats/ups.js',
|
||||
'./formats/bps.js',
|
||||
'./formats/rup.js',
|
||||
|
@ -26,8 +27,10 @@ self.onmessage = event => { // listen for messages from the main thread
|
|||
patch=parseIPSFile(patchFile);
|
||||
}else if(header.startsWith(UPS_MAGIC)){
|
||||
patch=parseUPSFile(patchFile);
|
||||
}else if(header.startsWith(APS_MAGIC)){
|
||||
}else if(header.startsWith(APS_N64_MAGIC)){
|
||||
patch=parseAPSFile(patchFile);
|
||||
}else if(header.startsWith(APS_GBA_MAGIC)){
|
||||
patch=APSGBA.fromFile(patchFile);
|
||||
}else if(header.startsWith(BPS_MAGIC)){
|
||||
patch=parseBPSFile(patchFile);
|
||||
}else if(header.startsWith(RUP_MAGIC)){
|
||||
|
|
|
@ -4,7 +4,7 @@ self.importScripts(
|
|||
'./MarcFile.js',
|
||||
'./crc.js',
|
||||
'./formats/ips.js',
|
||||
'./formats/aps.js',
|
||||
'./formats/aps_n64.js',
|
||||
'./formats/ups.js',
|
||||
'./formats/bps.js',
|
||||
'./formats/ppf.js',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue