mirror of
https://github.com/marcrobledo/savegame-editors.git
synced 2025-04-24 16:35:10 +00:00
295 lines
7.5 KiB
JavaScript
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);
|
|
}
|
|
}
|
|
}
|
|
}()); |