/* The legend of Zelda: Breath of the wild v20200215 by Marc Robledo 2017-2019 */ var currentEditingItem=0; SavegameEditor={ Name:'The legend of Zelda: Breath of the wild', Filename:'game_data.sav', Version:20190625, /* Constants */ Constants:{ MAX_ITEMS:410, STRING_SIZE:0x20, STRING64_SIZE:0x80, //missing versions: 1.1.1, 1.1.2 and 1.4.1 VERSION: ['v1.0', 'v1.1', 'v1.2', 'v1.3', 'v1.3.1', 'Kiosk', 'v1.3.3','v1.3.4', 'v1.4', 'v1.5', 'v1.5*', 'v1.6', 'v1.6*', 'v1.6**','v1.6***'], FILESIZE: [896976, 897160, 897112, 907824, 907824, 916576, 1020648, 1020648, 1027208, 1027208, 1027248, 1027216, 1027216, 1027216, 1027216], HEADER: [0x24e2, 0x24ee, 0x2588, 0x29c0, 0x2a46, 0x2f8e, 0x3ef8, 0x3ef9, 0x471a, 0x471b, 0x471b, 0x471e, 0x0f423d, 0x0f423e,0x0f423f], ICON_TYPES:{SWORD: 27, BOW:28, SHIELD:29, POT:30, STAR:31, CHEST:32,SKULL:33,LEAF:34,TOWER:35} }, /* Hashes */ Hashes:[ 0x0bee9e46, 'MAP', 0x0cbf052a, 'FLAGS_BOW', 0x1e3fd294, 'FLAGSV_BOW', 0x23149bf8, 'RUPEES', 0x2906f327, 'MAX_HEARTS', 0x333aa6e5, 'HORSE_SADDLES', 0x3adff047, 'MAX_STAMINA', 0x441b7231, 'DEFEATED_MOLDUGA_COUNTER', 0x54679940, 'DEFEATED_HINOX_COUNTER', 0x57ee221d, 'FLAGS_WEAPON', 0x5f283289, 'ITEMS', 0x6150c6be, 'HORSE_REINS', 0x698266be, 'DEFEATED_TALUS_COUNTER', 0x69f17e8a, 'FLAGSV_SHIELD', 0x6a09fc59, 'ITEMS_QUANTITY', 0x73c29681, 'PLAYTIME', 0x7b74e117, 'HORSE_NAMES', 0x8a94e07a, 'KOROK_SEED_COUNTER', 0x9383490e, 'MapApp_MapIconNo', 0x97f925c3, 'RELIC_GERUDO', 0x982ba201, 'HORSE_POSITION', 0x9c6cfd3f, 'HORSE_MANES', 0xa40ba103, 'PLAYER_POSITION', 0xa6d926bc, 'FLAGSV_WEAPON', 0xc247b696, 'HORSE_TYPES', 0xc5238d2b, 'FLAGS_SHIELD', 0xc9328299, 'MOTORCYCLE', /* IsGet_Obj_Motorcycle */ 0xce7afed3, 'MONS', 0xd913b769, 'MAPTYPE', 0xe1a0ca54, 'HORSE_BONDS', /* max=0x3f80 */ 0xea9def3f, 'MapApp_MapIconPos', 0xf1cf4807, 'RELIC_GORON', 0xfda0cde4, 'RELIC_RITO' ], Headers:{ 'MapApp_MapIconNo':0x9383490e, 'MapApp_MapIconPos':0xea9def3f }, /* private functions */ _toHexInt:function(i){var s=i.toString(16);while(s.length<8)s='0'+s;return '0x'+s}, _writeBoolean:function(offset,val,arrayPos){if(arrayPos)tempFile.writeU32(offset+8*arrayPos,val?1:0);else tempFile.writeU32(offset,val?1:0)}, _writeValue:function(offset,val,arrayPos){if(arrayPos)tempFile.writeU32(offset+8*arrayPos,val);else tempFile.writeU32(offset,val)}, _writeFloat32:function(offset,val,arrayPos){if(arrayPos)tempFile.writeF32(offset+8*arrayPos,val);else tempFile.writeF32(offset,val)}, _writeString:function(offset,str,len){ len=len || 8; for(var i=0; i'+itemId+'' }, _getItemCategory:function(itemId){ for(var i=0; i=896976 && tempFile.fileSize<=1500000) && versionHash===this.Constants.HEADER[i] && tempFile.readU32(4)===0xffffffff){ /* check for mods, filesizes vary */ this._getOffsets(i); setValue('version', this.Constants.VERSION[i]+'mod ('+CONSOLE+')'); return true; } } return false }, checkValidSavegame:function(){ return this._checkValidSavegameByConsole(false) || this._checkValidSavegameByConsole(true); }, preload:function(){ this.selectItem=document.createElement('select'); this.selectItem.addEventListener('blur', function(){ //console.log('blur'); SavegameEditor.editItem2(currentEditingItem, this.value); this.parentElement.removeChild(this); currentEditingItem=null; }, false); setNumericRange('rupees', 0, 999999); setNumericRange('mons', 0, 999999); setNumericRange('relic-gerudo', 0, 99); setNumericRange('relic-goron', 0, 99); setNumericRange('relic-rito', 0, 99); /* prepare edit item selector */ this.selectItem.categories={}; for(var i=0; iYou can now safely remove all .jpg files under pict_book folder.'); } var mapPinCount = 0; var MAX_MAP_PINS = 100; function loadMapPins(){ // Read Pin Types var count = 0; iterateMapPins(function(val){ if (val == 0xffffffff){ return false; } count++; //console.log(count, val) return true; }) // to debug saved locations // var i = 0; // iterateMapPinLocations(function(val, offset){ // if (i % 3 == 0){ // console.log("-----") // if (val == -100000){ // return false; // } // } // i++ // console.log(val) // return true // }) mapPinCount = count; setValue('number-map-pins', count); } function guessMainFieldGrid() { if (getValue('pos-maptype') == "MainField") setValue("pos-map",guessMainFieldGridInternal(getValue("pos-x"), getValue("pos-z"))) } function fixDungeonCoordinates() { var dungeon = getValue('pos-map') if (dungeon == "RemainsFire") { setValue('pos-x', 0) setValue('pos-y',16.8) setValue('pos-z',69.5) } else if (dungeon == "RemainsWater") { setValue('pos-x',47.7) setValue('pos-y',6.05) setValue('pos-z',6.3) } else if (dungeon == "RemainsWind") { setValue('pos-x',0) setValue('pos-y',3.4) setValue('pos-z',-77.7) } else if (dungeon == "RemainsElectric") { setValue('pos-x',0) setValue('pos-y',71.9) setValue('pos-z',3.7) } else if (dungeon == "FinalTrial") { setValue('pos-x',0) setValue('pos-y',-0.4) setValue('pos-z',64.5) } } function guessMainFieldGridInternal(xpos, zpos) { // A1 = -4974.629, -3974.629 // J8 = 4974.629, 3974.629 // X and letter part of grid: west/east // Z and number part of grid: north/south // grid also visible at https://mrcheeze.github.io/botw-object-map/ // idea: Take position fraction out of the whole grid and divide equally. var gridvalX = Math.min(10, Math.max(1, Math.trunc((xpos + 4974.629) / 9949.258 * 10 + 1))) var gridvalZ = Math.min( 8, Math.max(1, Math.trunc((zpos + 3974.629) / 7949.258 * 8 + 1))) return String.fromCharCode(64 + gridvalX) + '-' + gridvalZ } function clearMapPins(){ // types var count = 0; iterateMapPins(function(val,offset){ if (val != 0xffffffff){ count++; tempFile.writeU32(offset, 0xffffffff) } return true; }) var count2 =0; var i = 0; iterateMapPinLocations(function(val, offset){ var expect = i % 3 == 0 ? -100000 : 0; i++; if (val != expect){ count2++ tempFile.writeF32(offset, expect) } return true }) if (count2 / 3 > count){ count = count2 / 3 } mapPinCount = 0; setValue('number-map-pins', 0); MarcDialogs.alert(count+' map pins removed'); } function iterateMapPins(f){ var offset = SavegameEditor.Offsets.MapApp_MapIconNo; for (var i = 0;; i++){ var base = offset + (8 * i) var hdr = tempFile.readU32(base) var val = tempFile.readU32(base + 4) if (hdr != SavegameEditor.Headers.MapApp_MapIconNo){ break } if (!f(val,base+4)){ break } } } function iterateMapPinLocations(f){ offset = SavegameEditor.Offsets.MapApp_MapIconPos; for (var i = 0;; i++){ var base = offset + (8 * i) var hdr = tempFile.readU32(base) var val = tempFile.readF32(base + 4) if (hdr != SavegameEditor.Headers.MapApp_MapIconPos){ break } if(!f(val,base+4)){ break } } } function dist(px,py,pz,l){ // 2d seems to work better than 3d return Math.sqrt((Math.pow(l[0]-px,2))+(Math.pow(l[2]-pz,2))) } function addToMap(data, icon){ var px=tempFile.readF32(SavegameEditor.Offsets.PLAYER_POSITION); var py=tempFile.readF32(SavegameEditor.Offsets.PLAYER_POSITION+8); var pz=tempFile.readF32(SavegameEditor.Offsets.PLAYER_POSITION+16); var points = []; for (var i = 0; ih){ document.getElementById('header').style.position='fixed'; document.getElementById('header').style.top='-'+h+'px'; }else{ document.getElementById('header').style.position='absolute'; document.getElementById('header').style.top='0px'; } } window.addEventListener('scroll', onScroll, false); if(typeof String.endsWith==='undefined'){ String.prototype.endsWith=function(search){ return (new RegExp(search+'$')).test(this) }; } if(typeof String.startsWith==='undefined'){ String.prototype.startsWith=function(search){ return (new RegExp('^'+search)).test(this) }; } var masterModeLoaded=false; function loadMasterMode(){ if(!masterModeLoaded){ var script=document.createElement('script'); script.type='text/javascript'; script.src='./zelda-botw.master.js'; script.onload=function(){ masterModeLoaded=true; document.getElementById('tab-button-master').disabled=false; //BOTWMasterEditor.prepare(); }; document.getElementsByTagName('head')[0].appendChild(script); } }