0
0
mirror of https://github.com/marcrobledo/savegame-editors.git synced 2025-04-24 16:35:10 +00:00
nintendo-savegame-editors/zelda-botw/zelda-botw.master.js

295 lines
7.5 KiB
JavaScript

/*
The legend of Zelda: Breath of the wild - Master editor v20190625
by Marc Robledo 2017-2019
datamining by MrCheeze:
https://raw.githubusercontent.com/MrCheeze/botw-tools/master/gamedata.json
*/
const BOOL=1;
const BOOL_ARRAY=2;
const S32=3;
const S32_ARRAY=4;
const F32=5;
const F32_ARRAY=6;
const VECTOR2F=7;
const VECTOR2F_ARRAY=8;
const VECTOR3F=9;
const VECTOR3F_ARRAY=10;
const VECTOR4F=11;
const VECTOR4F_ARRAY=12;
const STRING=13;
const STRING_ARRAY=14;
const STRING64=15;
const STRING64_ARRAY=16;
const STRING256=17;
const STRING256_ARRAY=18;
function addEditorRow(container, left, right){
var tr=document.createElement('tr');
tr.appendChild(document.createElement('td'));
tr.appendChild(document.createElement('td'));
tr.children[1].className='text-right';
if(right){
tr.children[0].appendChild(label(right.id, left));
tr.children[1].appendChild(right);
}else{
tr.children[0].innerHTML=left;
}
container.appendChild(tr);
}
function createHashInput(container, hashId, type, offset){
if(type===BOOL){
var c=checkbox(hashId);
c.offset=offset+4;
c.addEventListener('change', setBoolean);
if(tempFile.readU32(offset+4))
c.checked=true;
addEditorRow(container, hashId, c);
return 8;
}else if(type===S32){
var inp=inputNumber(hashId, 0, 0xffffffff, tempFile.readU32(offset+4));
inp.offset=offset+4;
inp.addEventListener('change', setS32);
addEditorRow(container, hashId, inp);
return 8;
}else if(type===F32){
var inp=inputNumber(hashId, 0, 0xffffffff, tempFile.readF32(offset+4));
inp.offset=offset+4;
inp.addEventListener('change', setF32);
addEditorRow(container, hashId, inp);
return 8;
}else if(type===VECTOR2F){
createHashInput(container, hashId+'[x]', F32, offset);
createHashInput(container, hashId+'[y]', F32, offset+8);
return 16;
}else if(type===VECTOR3F){
createHashInput(container, hashId+'[x]', F32, offset);
createHashInput(container, hashId+'[y]', F32, offset+8);
createHashInput(container, hashId+'[z]', F32, offset+16);
return 24;
}else if(type===VECTOR4F){
createHashInput(container, hashId+'[x]', F32, offset);
createHashInput(container, hashId+'[y]', F32, offset+8);
createHashInput(container, hashId+'[z]', F32, offset+16);
createHashInput(container, hashId+'[t]', F32, offset+24);
return 32;
}else if(type===STRING){
var inp=input(hashId, SavegameEditor._readString(offset+4));
inp.offset=offset+4;
inp.maxLength=32;
inp.addEventListener('change', setString);
addEditorRow(container, hashId, inp);
return 0x20;
}else if(type===STRING64){
var inp=input(hashId, SavegameEditor._readString64(offset+4));
inp.offset=offset+4;
inp.maxLength=64;
inp.addEventListener('change', setString64);
addEditorRow(container, hashId, inp);
return 0x80;
}else if(type===STRING256){
var inp=input(hashId, SavegameEditor._readString256(offset+4));
inp.offset=offset+4;
inp.maxLength=256;
inp.addEventListener('change', setString256);
addEditorRow(container, hashId, inp);
return 0x0200;
}else if(type && type%2===0){ /* array */
var hash=tempFile.readU32(offset);
var nextHash=hash;
var i=0;
var size=0;
while(nextHash===hash){
size=createHashInput(container, hashId+'['+i+']', type-1, offset+i*size);
i++;
nextHash=tempFile.readU32(offset+i*size);
}
}else{
addEditorRow(container, hashId+' (unknown)');
}
}
function setBoolean(){
SavegameEditor._writeBoolean(this.offset, this.checked, 0);
}
function setS32(){
SavegameEditor._writeValue(this.offset, this.value, 0);
}
function setF32(){
SavegameEditor._writeFloat32(this.offset, this.value, 0);
}
function setString(){
SavegameEditor._writeString(this.offset, this.value);
}
function setString64(){
SavegameEditor._writeString64(this.offset, this.value);
}
function setString256(){
SavegameEditor._writeString256(this.offset, this.value);
}
var BOTWMasterEditor=(function(){
var HASHES_PER_PAGE=100;
var loaded=false;
var HASHES=[];
var parseHashFile=function(responseText){
var lines=responseText.split('\n');
for(var i=0; i<lines.length; i++){
var data=lines[i].split(';');
HASHES[parseInt(data[0])]=[
parseInt(data[1]),
data[2]
];
}
document.getElementById('master-editor-loading').style.display='none';
document.getElementById('master-editor-hidden').style.display='block';
BOTWMasterEditor.refreshResults();
};
return{
isLoaded:function(){
return loaded;
},
loadHashes:function(){
if(typeof window.fetch==='function'){
fetch('./zelda-botw.hashes.csv')
.then(res => res.text()) // Gets the response and returns it as a blob
.then(responseText => {
parseHashFile(responseText);
})
.catch(function(){
alert('Unexpected error: can\'t download hash file');
});
}else{
var oReq=new XMLHttpRequest();
oReq.open('GET', './zelda-botw.hashes.csv', true);
oReq.responseType='text';
oReq.onload=function(oEvent){
if(this.status===200) {
parseHashFile(responseText);
}else{
alert('Unexpected error: can\'t download hash file');
}
};
oReq.onerror=function(oEvent){
alert('Unexpected error: can\'t download hash file');
};
oReq.send(null);
}
},
filterHashes:function(regexFilter){
var findHashesIn;
empty('table');
this.filteredHashes=[];
if(regexFilter){
findHashesIn={};
for(var hash in HASHES){
if(regexFilter.test(HASHES[hash][1])){
findHashesIn[hash]=true;
}
}
}else{
findHashesIn=HASHES;
}
var previousHashValue=0;
for(var i=0x0c; i<tempFile.fileSize-4; i+=8){
var hashValue=tempFile.readU32(i);
if(hashValue===previousHashValue)
continue;
previousHashValue=hashValue;
if(findHashesIn[hashValue]){
this.filteredHashes.push({
type:HASHES[hashValue][0],
id:HASHES[hashValue][1],
offset:i
});
}else if(regexFilter===-1){
this.filteredHashes.push({
id:'* '+this._toHexInt(hashValue)
});
}
}
this.filteredHashes.sort(function(a,b){
if(a.id<b.id)return -1;
if(a.id>b.id)return 1;
return 0;
});
this.currentPage=0;
this.nPages=parseInt(this.filteredHashes.length/HASHES_PER_PAGE);
if(this.filteredHashes.length%HASHES_PER_PAGE!==0){
this.nPages++;
}
empty('select-page');
for(var i=0; i<this.nPages; i++){
var option=document.createElement('option');
option.innerHTML=i+1;
document.getElementById('select-page').appendChild(option);
}
this.setPage(0);
return this.filteredHashes;
},
setPage:function(newPage){
if(newPage>=0 && newPage<this.nPages){
document.getElementById('select-page').selectedIndex=newPage;
this.currentPage=newPage;
var filteredHashes=this.filteredHashes.slice(this.currentPage*HASHES_PER_PAGE, HASHES_PER_PAGE+this.currentPage*HASHES_PER_PAGE);
empty('table');
var container=document.createElement('tbody');
for(var i=0; i<filteredHashes.length; i++){
var hash=filteredHashes[i];
if(hash.type){
createHashInput(container, hash.id, hash.type, hash.offset)
}else{
addEditorRow(container, hash.id);
}
}
get('table').appendChild(container);
}
},
prevPage:function(){this.setPage(this.currentPage-1);},
nextPage:function(){this.setPage(this.currentPage+1);},
refreshResults:function(){
var filter=getValue('filters').trim();
var customFilter=getValue('custom-filter').trim();
if(filter){
this.filterHashes(new RegExp(filter));
}else if(customFilter){
this.filterHashes(new RegExp(customFilter, 'i'));
}else{
this.filterHashes(null);
}
}
}
}());