os.execute("chcp 65001") os.execute("cls") local bit = require("luabit") local inspect = require("inspect") local compressor = require("LibCompress") local MapNames = {"1st Floor","2nd Floor","3rd Floor","4th Floor","5th Floor","Cave of Wisdom","Wisdom Underground","Cave of Truth","Cave of Truth","Cave of Strength","Strength+Courage Underground","Cave of Courage"}; local AttackNames = {"Rock Throw", "Blaze 1", "Blaze 2", "Blaze 3", "Blaze 4", "Freeze 1", "Freeze 2", "Freeze 3", "Freeze 4", "Bolt 1", "Bolt 2", "Bolt 3", "Blast 1", "Blast 2", "Blast 3", "Burst 1", "Burst 2", "Burst 3", "Quick 1", "Quick 2", "Slow 1", "Slow 2", "Muddle 1", "Muddle 2", "Sleep 1", "Sleep 2", "Screen 1", "Desoul 1", "Heal 1", "Heal 2", "Heal 3", "Revive 2", "Attack (poison)", "Lunge (poison)", "Poison Powder", "Lash Out (poison)", "Attack (paralysis)", "Lunge (paralysis)", "Paralyzing Powder", "Paralyzing Mist", "Attack (sleep)", "Sleep Powder", "Sleep Mist", "Attack (normal)", "Attack (critical 1)", "Attack (critical 2)", "Attack (hero)", "Attack (hero)", "Attack (hero)", "Attack (hero)", "Attack (hero)", "Scorching Hot Air", "Crimson Flames", "White-Hot Gas", "Icy Breath", "Freezing Cloud", "Freezing Gas", "Defend (shield)", "Defend (robe)", "Defend (muscles)", "Watch Warily", "Wait", "Wary Eyes", "Flee", "Back Away", "Eerie Dance", "One Massive Force", "Unleash Their Flames", "Strange Aura", "Concentration", "Help! (same type)", "Help! (same area)", "Help! (Dreampuff)", "Help! (Fungoids)", "Help! (Meat Zombie)", "Explode", "Fission", "Sticky Web", "Kick Out (normal)", "Kick Out (critical)", "Heal 1", "Heal 2", "Heal 3", "Lunge (normal)", "Lash Out (normal)", "Wing Flap", "Savage Wing Flap", "Boulder Throw", "Bloodshot Eyes", "Ball of Flame", "Desoul 2", "Howl in Pain", "Attack (instant kill)", "Arrow", "Demonbreath 2", "Demonbreath 1", "Attack (Dark Knight)", "Bolt 3 (Dark Sol)", "Attack (Dark Sol 2)", "Blaze 3 (Dark Sol 2)", "Bolt 3 (Dark Sol 2)", "Attack (Dark Sol 3)", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "Rock Throw", "Blaze 1", "Blaze 2", "Blaze 3", "Blaze 4", "Freeze 1", "Freeze 2", "Freeze 3", "Freeze 4", "Bolt 1", "Bolt 2", "Bolt 3", "Blast 1", "Blast 2", "Blast 3", "Burst 1", "Burst 2", "Burst 3", "Quick 1", "Quick 2", "Slow 1", "Slow 2", "Muddle 1", "Muddle 2", "Sleep 1", "Sleep 2", "Screen 1", "Desoul 1", "Heal 1", "Heal 2", "Heal 3", "Revive 2", "Attack (poison)", "Lunge (poison)", "Poison Powder", "Lash Out (poison)", "Attack (paralysis)", "Lunge (paralysis)", "Paralyzing Powder", "Paralyzing Mist", "Attack (sleep)", "Sleep Powder", "Sleep Mist", "Attack (normal)", "Attack (critical 1)", "Attack (critical 2)", "Attack (hero)", "Attack (hero)", "Attack (hero)", "Attack (hero)", "Attack (hero)", "Scorching Hot Air", "Crimson Flames", "White-Hot Gas", "Icy Breath", "Freezing Cloud", "Freezing Gas", "Defend (shield)", "Defend (robe)", "Defend (muscles)", "Watch Warily", "Wait", "Wary Eyes", "Flee", "Back Away", "Eerie Dance", "One Massive Force", "Unleash Their Flames", "Strange Aura", "Concentration", "Help! (same type)", "Help! (same area)", "Help! (Dreampuff)", "Help! (Fungoids)", "Help! (Meat Zombie)", "Explode", "Fission", "Sticky Web", "Kick Out (normal)", "Kick Out (critical)", "Heal 1", "Heal 2", "Heal 3", "Lunge (normal)", "Lash Out (normal)", "Wing Flap", "Savage Wing Flap", "Boulder Throw", "Bloodshot Eyes", "Ball of Flame", "Desoul 2", "Howl in Pain", "Attack (instant kill)", "Arrow", "Demonbreath 2", "Demonbreath 1", "Attack (Dark Knight)", "Bolt 3 (Dark Sol)", "Attack (Dark Sol 2)", "Blaze 3 (Dark Sol 2)", "Bolt 3 (Dark Sol 2)", "Attack (Dark Sol 3)"} -- https://gist.github.com/fernandohenriques/12661bf250c8c2d8047188222cab7e28 function hex2rgb (hex) local hex = hex:gsub("#","") if hex:len() == 3 then return (tonumber("0x"..hex:sub(1,1))*17)/255, (tonumber("0x"..hex:sub(2,2))*17)/255, (tonumber("0x"..hex:sub(3,3))*17)/255 else return tonumber("0x"..hex:sub(1,2))/255, tonumber("0x"..hex:sub(3,4))/255, tonumber("0x"..hex:sub(5,6))/255 end end local glyphs = ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\',!?-*/.()":#' local colors = { [1] = {hex2rgb("000000")}, [2] = {hex2rgb("e0e0e0")}, [3] = {hex2rgb("606060")}, [4] = {hex2rgb("a0a0a0")}, [5] = {hex2rgb("60a060")}, [6] = {hex2rgb("406040")}, [7] = {hex2rgb("A08000")}, [8] = {hex2rgb("e0e000")}, [9] = {hex2rgb("e00000")}, [10] = {hex2rgb("600000")}, [11] = {hex2rgb("a00000")}, [12] = {hex2rgb("e0c060")}, [13] = {hex2rgb("808000")}, [14] = {hex2rgb("404000")}, [15] = {hex2rgb("000000")}, [16] = {hex2rgb("0000e0")}, } colors[1] = {0,0,0,0} local MapAddresses = {} local EnemyTiles = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'} local ROM = {} function to_number(value) return value and 1 or 0 end function ROM:load(filename) print ("Loading ROM: ".. filename) self.filename = filename self.file, self.err = io.open(filename,"rb") self.filesize = self.file:seek("end") print ("Size: ".. self.filesize) self.file:seek("set") self.enemy_maps = {} self.maps = {} self.data = {} self.enemies = {} self.items = {} self.total_chests = 129 self.total_stairs = 19 self.total_items = 127 -- there are 127 items (0-126), data goes further, but it's enemy data self.total_enemies = 177 -- there are 126 of them (0-125), but the data goes further, so why not? for offset = 1, ROM.filesize do ROM.data[offset] = ROM.file:read(1) end self.map_names = MapNames print ("Loading maps (".. #self.map_names..")") self:load_maps() print ("Loading stairs (".. self.total_stairs..")*2") self:load_stairs() print ("Loading items (".. self.total_items..")") self:load_item_icons() self:load_items() print ("Loading items (".. self.total_chests..")") self:load_chests() print ("Loading enemies (".. self.total_enemies..")") self:load_enemies() print("Done") end function ROM:load_chests() local chests = {} local Spacer = 1 for I = 0, self.total_chests do chests[Spacer] = chests[Spacer] or {} local ItemNum = "" if self.data[33990 + I]:byte() < 128 then local Offset = self.data[33990 + I]:byte() chests[Spacer][#chests[Spacer]+1] = self.items[Offset+1] elseif self.data[33990 + I]:byte() < 255 then local C = self.data[33990 + I]:byte() - 128 local Iname = "" if C == 0 then Iname = 50 elseif C == 1 then Iname = 100 elseif C == 2 then Iname = 200 elseif C == 3 then Iname = 300 elseif C == 4 then Iname = 500 elseif C == 5 then Iname = 1000 elseif C == 6 then Iname = 2000 elseif C == 7 then Iname = self.enemies[124] elseif C == 8 then Iname = self.enemies[123] elseif C == 9 then Iname = self.enemies[122] else Iname = "UNKNOWN=" + string.char(C) end chests[Spacer][#chests[Spacer]+1] = Iname else Spacer = Spacer + 1 end end -- chests contain actual tables of items/enemies or a number in case of gold -- items can be distingueshed from enemies by checking chests[i].actions or any other field -- if type(chests[i]) == number - it's gold -- else do that check and spawn enemy if needed self.chests = chests end function ROM:load_maps() for map_id = 1, #self.map_names do local doors = {} local temp = {} local offset = 612075 --612074 local z, repeat_, repeat_val = 0,0,0; local c = 1; local x,y local enemy_table = {} local map_start_ofset = 16777216 * ROM.data[offset + (map_id-1) * 4 + 0]:byte() + 65536 * ROM.data[offset + (map_id-1) * 4 + 1]:byte() + 256 * ROM.data[offset + (map_id-1) * 4 + 2]:byte() + ROM.data[offset + (map_id-1) * 4 + 3]:byte() +1 local additional_offset = map_start_ofset + 129 --print("map_start_ofset ", map_start_ofset) --print("additional_offset ", additional_offset) --print("ROM.data[map_start_ofset]",self.data[map_start_ofset]) local data = {} for y = 1, 32 do z = 16777216 * ROM.data[map_start_ofset + (y-1) * 4 + 0]:byte() + 65536 * ROM.data[map_start_ofset + (y-1) * 4 + 1]:byte() + 256 * ROM.data[map_start_ofset + (y-1) * 4 + 2]:byte() + ROM.data[map_start_ofset + (y-1) * 4 + 3]:byte() for x=1, 32 do if bit.band(z,1) == 0 then data[33-x] = data[33-x] or {} data[33-x][y] = 192 else data[33-x] = data[33-x] or {} data[33-x][y] = 1 end z = bit.rshift(z, 1) end end for y = 1, 32 do for x = 1, 32 do temp[c] = 0 if (data[x][y] == 1) then if (repeat_ == 0) then temp[c] = bit.band(ROM.data[additional_offset]:byte(), 191) if (bit.band(ROM.data[additional_offset]:byte(), 64) > 0) then repeat_val = temp[c]; additional_offset = additional_offset +1; repeat_ = ROM.data[additional_offset]:byte(); end additional_offset = additional_offset +1; else temp[c] = repeat_val; repeat_ = repeat_ -1; end c = c + 1 end end end c = 1; if (ROM.data[map_start_ofset + 128]:byte() == 0) then for x= 1, 32 do for y = 1, 32 do if (data[y][x] == 1) then data[y][x] = temp[c]; c = c+1 end end end else for y = 1, 32 do for x = 1, 32 do if (data[y][x] == 1) then data[y][x] = temp[c]; c = c+1 end end end end io.write("layout, ") -- map's enemy data for i = 1, 16 do enemy_table[i] = ROM.data[additional_offset]:byte(); additional_offset = additional_offset + 1; end local enemy_offset = additional_offset; local D2, D1, D0 = 0,0,0; local done = false local additional_offset = 0; local XEMap = {} local YEMap = {} local enemy_map = {} c = 0; for x = 1, 32 do for y = 1, 32 do XEMap[x] = XEMap[x] or {} if (bit.band(data[x][y], 64) == 0) then XEMap[x][y] = c; c = c+1; else XEMap[x][y] = -1; end end end c = 0; for y = 1, 32 do for x = 1, 32 do YEMap[x] = YEMap[x] or {} if (bit.band(data[x][y], 64) == 0) then YEMap[x][y] = c; c = c+1; else YEMap[x][y] = -1; end end end if (ROM.data[enemy_offset]:byte() == 1) then for x = 1, 32 do for y = 1, 32 do enemy_map[x] = enemy_map[x] or {} if (XEMap[x][y] == -1) then enemy_map[x][y] = 255; else D2 = 0; done = false; D0 = XEMap[x][y]; additional_offset = enemy_offset + 1; while (not done) do D1 = ROM.data[additional_offset]:byte(); if (D1 > 127) then D2 = D2 + bit.band(D1, 127); additional_offset = additional_offset + 1; end if (D2 >= D0) then done = true; else D2 = D2 + 1; additional_offset = additional_offset + 1; end end D0 = bit.band(ROM.data[additional_offset]:byte(), 15); enemy_map[x][y] = D0; end end end else for y = 1, 32 do for x = 1, 32 do enemy_map[x] = enemy_map[x] or {} if (YEMap[x][y] == -1) then enemy_map[x][y] = 255; else D2 = 0; done = false; additional_offset = enemy_offset + 1; while (not done) do D1 = ROM.data[additional_offset]:byte(); if (D1 > 127) then D2 = D2 + bit.band(D1, 127); additional_offset = additional_offset + 1; end if (D2 >= (YEMap[x][y])) then done = true; else D2 = D2 + 1; additional_offset = additional_offset + 1; end end D0 = bit.band(ROM.data[additional_offset]:byte(), 15); enemy_map[x][y] = D0; end end end end io.write("enemies, ") local objects = {} for y = 1, 32 do --print() for x = 1, 32 do --io.write(string.rep(" ",3- string.len(data[x][y]))..(data[x][y]).." ") -- if #objects[ID] is 0, there's no objects of that type! if data[x][y] == 010 then objects[010] = objects[010] or {type="moss"} objects[010][#objects[010]+1] = {y,x} elseif data[x][y] == 014 then objects[014] = objects[014] or {type="puddle"} objects[014][#objects[014]+1] = {y,x} elseif data[x][y] == 028 then objects[028] = objects[028] or {type="torch1"} objects[028][#objects[028]+1] = {y,x} elseif data[x][y] == 134 then objects[134] = objects[134] or {type="chest"} objects[134][#objects[134]+1] = {y,x} elseif data[x][y] == 129 then objects[129] = objects[129] or {type="greendoor"} objects[129][#objects[129]+1] = {y,x} doors[#doors+1] = {y,x} elseif data[x][y] == 130 then objects[130] = objects[130] or {type="greydoor"} objects[130][#objects[130]+1] = {y,x} doors[#doors+1] = {y,x} elseif data[x][y] == 150 then objects[150] = objects[150] or {type="angelwall"} objects[150][#objects[150]+1] = {y,x} doors[#doors+1] = {y,x} elseif data[x][y] == 131 then objects[131] = objects[131] or {type="jaildoor"} objects[131][#objects[131]+1] = {y,x} doors[#doors+1] = {y,x} elseif data[x][y] == 009 then objects[009] = objects[009] or {type="spinner"} objects[009][#objects[009]+1] = {y,x} elseif data[x][y] == 144 then objects[144] = objects[144] or {type="goldfountain"} objects[144][#objects[144]+1] = {y,x} elseif data[x][y] == 029 then objects[029] = objects[029] or {type="groundtorch"} objects[029][#objects[029]+1] = {y,x} elseif data[x][y] == 132 then objects[132] = objects[132] or {type="stairsup"} objects[132][#objects[132]+1] = {y,x} elseif data[x][y] == 008 then objects[008] = objects[008] or {type="floorhole"} objects[008][#objects[008]+1] = {y,x} elseif data[x][y] == 007 then objects[007] = objects[007] or {type="floortrap"} objects[007][#objects[007]+1] = {y,x} elseif data[x][y] == 027 or data[x][y] == 155 or data[x][y] == 151 then -- CHECK HOW walls on LVLs 3/4 is diferent! objects[027] = objects[027] or {type="fakewall"} objects[027][#objects[027]+1] = {y,x} elseif data[x][y] == 147 then objects[147] = objects[147] or {type="sentinel"} objects[147][#objects[147]+1] = {y,x} elseif data[x][y] == 139 then objects[139] = objects[139] or {type="onewaydoor"} objects[139][#objects[139]+1] = {y,x} doors[#doors+1] = {y,x} elseif data[x][y] == 133 then objects[133] = objects[133] or {type="stairsdown"} objects[133][#objects[133]+1] = {y,x} elseif data[x][y] == 143 then objects[143] = objects[143] or {type="fountain1"} objects[143][#objects[143]+1] = {y,x} elseif data[x][y] == 020 then objects[020] = objects[020] or {type="ceilinghole"} objects[020][#objects[020]+1] = {y,x} elseif data[x][y] == 021 then objects[021] = objects[021] or {type="special"} objects[021][#objects[021]+1] = {y,x} elseif data[x][y] == 146 then objects[146] = objects[146] or {type="dropping wall"} -- a wall drops before you if you try to walk objects[146][#objects[146]+1] = {y,x} doors[#doors+1] = {y,x} -- NOT a door, but should work as an inverted cell door ;) elseif data[x][y] == 152 then objects[152] = objects[152] or {type="spirit fountain"} objects[152][#objects[152]+1] = {y,x} elseif data[x][y] == 54 then objects[54] = objects[54] or {type="darksol fight"} objects[54][#objects[54]+1] = {y,x} elseif data[x][y] == 54 then objects[23] = objects[23] or {type="false idol indent"} objects[23][#objects[23]+1] = {y,x} end if data[x][y]~=192 and data[x][y]~=000 and data[x][y]~=042 -- see moss and data[x][y]~=046 -- see puddle and data[x][y]~=060 -- see torch and data[x][y]~=033 -- see green door and data[x][y]~=034 -- see grey door and data[x][y]~=038 -- see chest and data[x][y]~=035 -- see cell door and data[x][y]~=041 -- see spinner and data[x][y]~=048 -- see gold fountain and data[x][y]~=061 -- see ground torch and data[x][y]~=036 -- see stairs up and data[x][y]~=040 -- see floor hole and data[x][y]~=039 -- see floor trap and data[x][y]~=059 -- see fakewall and data[x][y]~=051 -- see sentinel and data[x][y]~=043 -- see one way door and data[x][y]~=037 -- see stairs down and data[x][y]~=047 -- see fountain and data[x][y]~=052 -- see ceiling hole and data[x][y]~=053 -- see special (truth orb, etc) and data[x][y]~=055 -- see special 2 and data[x][y]~=057 -- see special 2 \___ level 4 mystery, there's NOTHING in ANY version of the game (that loads) and data[x][y]~=025 -- see special 2 / and data[x][y]~=050 -- see falling wall and data[x][y]~=056 -- see spirit fountain then objects.total = (objects.total or 0 )+1 end local count2 = 0 for id, obj in pairs(objects) do if type(obj) == "table" then count2 = count2+#obj end end objects.total2 = count2 end end --print() io.write("objects ("..objects.total..") ",objects.total2) self.enemy_maps[map_id] = enemy_map self.maps[map_id] = data self.maps[map_id].id = map_id self.maps[map_id].objects = objects self.maps[map_id].name = self.map_names[map_id] self.maps[map_id].enemy_table = enemy_table self.maps[map_id].enemy_map = enemy_map self.maps[map_id].doors = doors io.write(".\n") end end function ROM:load_stairs() self.stairs = {} local stairs_offset = 24901 -- again, +1, since Lua is 1-based for current_offset = 0, self.total_stairs-1 do -- forth self.stairs[#self.stairs+1] = { ROM.data[stairs_offset + current_offset * 6 + 2]:byte()+1, -- starting floor (+1 for 1-based Lua) ROM.data[stairs_offset + current_offset * 6 + 1]:byte()+1, -- starting x position ROM.data[stairs_offset + current_offset * 6 + 0]:byte()+1, -- statring y position ROM.data[stairs_offset + current_offset * 6 + 5]:byte()+1, -- target floor ROM.data[stairs_offset + current_offset * 6 + 4]:byte()+1, -- target x position ROM.data[stairs_offset + current_offset * 6 + 3]:byte()+1, -- target y position } local stair = self.stairs[#self.stairs] print(stair[1], stair[2], stair[3], stair[4], stair[5], stair[6]) -- back self.stairs[#self.stairs+1] = { ROM.data[stairs_offset + current_offset * 6 + 5]:byte()+1, -- target floor ROM.data[stairs_offset + current_offset * 6 + 4]:byte()+1, -- target x position ROM.data[stairs_offset + current_offset * 6 + 3]:byte()+1, -- target y position ROM.data[stairs_offset + current_offset * 6 + 2]:byte()+1, -- starting floor ROM.data[stairs_offset + current_offset * 6 + 1]:byte()+1, -- starting x position ROM.data[stairs_offset + current_offset * 6 + 0]:byte()+1, -- statring y position } local stair = self.stairs[#self.stairs] print(stair[1], stair[2], stair[3], stair[4], stair[5], stair[6]) end -- Dark Kobold's editors (yes, both; of any version; INCLUDING v09 ;p ) have a typo/error here -- stairs offset starts at 24900, not 24902 -- and the format is different: CORDINATES-floor to COORDINATES-floor end function ROM.map_char(char) local r = char if r >= 1 and r <= 9 then r = r + 19 end if r == 63 then r = 11 end if r == 67 then r = 17 end if r == 00 then r = 4 end if r == 28 then r = 114-28 end if r == 29 then r = 115-28 end if r == 70 then r = 46 - 28 end return r + 28 end function ROM:load_item_icons() local Offset = 933864+1 local item_icons = {} local item_icon_addresses = {} local item_icon_image_data = {} local off = 1 local items_count = self.total_items for id = 1, items_count do local ioff = 1 local Off = Offset + (id-1) * 192 local canvas = love.image.newImageData(16,32) local image = {} for x = 1, 8*4* 3 do -- 3 = rows in final image for y = 1, 8*4* 2 do -- 4 = cols in final image local off = Off + bit.lshift(x, 5) + bit.lshift(y,2) --( basically, Offset +4) image[ioff] = bit.rshift(ROM.data[off]:byte(), 4) image[ioff+1] = bit.band(ROM.data[off]:byte(), 15) off = off + 1 image[ioff+2] = bit.rshift(ROM.data[off]:byte(), 4) image[ioff+3] = bit.band(ROM.data[off]:byte(), 15) off = off + 1 image[ioff+4] = bit.rshift(ROM.data[off]:byte(), 4) image[ioff+5] = bit.band(ROM.data[off]:byte(), 15) off = off + 1 image[ioff+6] = bit.rshift(ROM.data[off]:byte(), 4) image[ioff+7] = bit.band(ROM.data[off]:byte(), 15) ioff = ioff + 8 end end local off = 1 for xx = 0, 2 do -- 3 = rows in final image for yy = 0, 1 do -- 4 = cols in final image for y = 0, 7 do for x = 0, 7 do local color = colors[(image[off] or 0)+1] love.graphics.setColor(color) canvas:setPixel(x+yy*8, y+xx*8, color[1], color[2], color[3], 1 ) off = off + 1 end end end end item_icons[id] = love.graphics.newImage(canvas) item_icon_image_data[id] = canvas item_icon_addresses[id] = Off end ROM.item_icons = item_icons ROM.item_icon_image_data = item_icon_image_data ROM.item_icon_addresses = item_icon_addresses end function ROM:load_items() -- the OG code didn't work for me. Dunno what I ported wrong... -- Anyway, item names are split into 2 tables: -- at 27108 and at 28123 (using +1, since lua's 1-based) -- 1st byte is the length of a substring (works for both parts) -- then N bytes of data that needs some conversion (ALL HAIL ROMHACKING TBL FILES!!!) -- add READ BYTES count to the starting OFFset -- rince and repeat local off = 27109 local of2 = 28124 for item_id = 1, self.total_items do local item_name="" -- 1st part local namelen = self.data[off]:byte() local tmp = {} local tmp2 = {} for i = 1, namelen-1 do local char = self.map_char(self.data[off+i]:byte()) tmp[#tmp+1] = string.char(char) end off = off + namelen -- 2nd part local namelen2 = self.data[of2]:byte() for i = 1, namelen2-1 do local char = self.map_char(self.data[of2+i]:byte()) if char == 0 then char = 4 end tmp[#tmp+1] = string.char(char) end of2 = of2 + namelen2 item_name=table.concat(tmp,"") -- merged with above to have 1 loop -- name data details hold true local data = self.data[8 * item_id + 4644]:byte() local Cursed = false local Arm = false local Weap = false local Helm = false local Shld = false local GroupAtt = false local BattleUse = false local OutUse = false local HEquip = false local MEquip = false local PEquip = false local Unknown1 = false local Unknown2 = false -- 10100001 means: cused, not out, yes in, ???, no group attack, ???, type 0->3 (3 =helm, 2= shield, 1= arm, 0=weap) if bit.band(data, 128) > 0 then Cursed = true end if bit.band(data, 64) > 1 then OutUse = true end if bit.band(data, 32) > 1 then BattleUse = true end if bit.band(data, 16) > 0 then Unknown1 = true end if bit.band(data, 8) > 0 then GroupAtt = true end if bit.band(data, 4) > 0 then Unknown2 = true end if bit.band(data, 3) == 3 then Helm = true end if bit.band(data, 3) == 2 then Shld = true end if bit.band(data, 3) == 1 then Arm = true end if bit.band(data, 3) == 0 then Weap = true end data = self.data[1 + 8 * item_id + 4644]:byte() if bit.band(data, 1) > 0 then HEquip = true end if bit.band(data, 2) > 0 then MEquip = true end if bit.band(data, 4) > 0 then PEquip = true end SpecialEff = math.floor(self.data[1 + 8 * item_id + 4644]:byte() / 8) Cost = 256 * self.data[2 + 8 * item_id + 4644]:byte() + self.data[3 + 8 * item_id + 4644]:byte() Mod1Value = self.data[4 + 8 * item_id + 4644]:byte() Mod2Value = self.data[6 + 8 * item_id + 4644]:byte() ModVal1 = self.data[5 + 8 * item_id + 4644]:byte() ModVal2 = self.data[7 + 8 * item_id + 4644]:byte() if Mod1Value == 128 then Mod1 = "Weapon Factor" elseif Mod1Value == 129 then Mod1 = "Armor Factor" elseif Mod1Value == 0 then Mod1 = "(none)" elseif Mod1Value == 144 then Mod1 = "Critical %" elseif Mod1Value == 145 then Mod1 = "# Attacks" else Mod1 = "Unknown - " .. Mod1Value end if Mod2Value == 128 then Mod2 = "Weapon Factor" elseif Mod2Value == 129 then Mod2 = "Armor Factor" elseif Mod2Value == 0 then Mod2 = "(none)" elseif Mod2Value == 144 then Mod2 = "Critical %" elseif Mod2Value == 145 then Mod2 = "# Attacks" else Mod2 = "Unknown - " .. Mod2Value end data = { id = item_id, name = item_name, cost = Cost, cursed = Cursed, type = {Weap, Helm, Arm, Shld, weapon = Weap, armor = Arm, helmet = Helm, shield = Shld}, group_attack = GroupAtt, usability = {BattleUse, OutUse, battle = BattleUse, outside = OutUse}, equip = {HEquip, MEquip, PEquip, hiro=HEquip, milo = MEquip, pyra = PEquip}, effect = SpecialEff, modifiers = {{Mod1Value, ModVal1, Mod1, Mod1Value = ModVal1}, {Mod2Value, ModVal2, Mod2, Mod2Value = ModVal2}}, ['equipable?'] = Unknown1, alchemist_item = Unknown2, bits = Bits, } self.items[item_id] = data end end function ROM:load_enemies() -- same as with item names, length, then N bytes. ONE part. Phew. -- again +1 to 28657 for 1-based Lua local name_offset = 28658 local enemy_offset = 216858 for enemy_id =0, self.total_enemies do local enemy_name = "" local namelen = self.data[name_offset]:byte() local tmp = {} for i = 1, namelen-1 do local char = self.map_char(self.data[name_offset+i]:byte()) tmp[#tmp+1] = string.char(char) end name_offset = name_offset + namelen enemy_name=table.concat(tmp,"") local ModelBox = bit.band(self.data[enemy_offset + 24 * enemy_id]:byte(), 252) / 4 local ColorFlip = bit.band(self.data[enemy_offset + 24 * enemy_id]:byte(), 3) * 64 + bit.band(self.data[enemy_offset + 24 * enemy_id + 1]:byte(), 252) / 4 local Flipud = false local Fliplr = false local chy = false local chb = false local chuer = false if bit.band(ColorFlip, 128) > 0 then Flipud = true end if bit.band(ColorFlip, 64) > 0 then Fliplr = true end if bit.band(ColorFlip, 32) > 0 then chy = true end if bit.band(ColorFlip, 16) > 0 then chb = true end if bit.band(ColorFlip, 8) > 0 then chuer = true end local Color = bit.band(ColorFlip, 7) local Un5s0 = bit.band(self.data[enemy_offset+ 24 * enemy_id+ 1]:byte(), 3) * 8 + bit.band(self.data[enemy_offset+ 24 * enemy_id+ 2]:byte(), 224) / 32 local Un7s0 = bit.band(self.data[enemy_offset+ 24 * enemy_id+ 2]:byte(), 31) * 4 + bit.band(self.data[enemy_offset+ 24 * enemy_id+ 3]:byte(), 192) / 64 local EXP = bit.band(self.data[enemy_offset+ 24 * enemy_id+ 3]:byte(), 63) * 64 + bit.band(self.data[enemy_offset+ 24 * enemy_id+ 4]:byte(), 252) / 4 local Gold = bit.band(self.data[enemy_offset+ 24 * enemy_id+ 4]:byte(), 3) * 1024 + bit.band(self.data[enemy_offset+ 24 * enemy_id+ 6]:byte(), 192) / 64 + (self.data[enemy_offset+ 24 * enemy_id+ 5]:byte()) * 4 local Drop = bit.band(self.data[enemy_offset+ 24 * enemy_id+ 6]:byte(), 63) * 2 + bit.band(self.data[enemy_offset+ 24 * enemy_id+ 7]:byte(), 128) / 128 local Rate = bit.band(self.data[enemy_offset+ 24 * enemy_id+ 7]:byte(), 112) / 16 local RateT = "1/" .. tostring(math.pow(2, (10 - Rate))) local RateV = 1/math.pow(2, (10 - Rate)) local Regen = bit.band(self.data[enemy_offset+ 24 * enemy_id+ 7]:byte(), 12) / 4 local RegenL = Regen * 50 local Un2s01 = bit.band(self.data[enemy_offset+ 24 * enemy_id + 7]:byte(), 3) local HP = self.data[enemy_offset+ 24 * enemy_id + 8]:byte() * 4 + bit.band(self.data[enemy_offset + 24 * enemy_id + 9]:byte(), 192) / 64 local MP = bit.band(self.data[enemy_offset + 24 * enemy_id + 9]:byte(), 63) * 16 + bit.band(self.data[enemy_offset + 24 * enemy_id + 10]:byte(), 240) / 16 local Ag = bit.band(self.data[enemy_offset + 24 * enemy_id + 10]:byte(), 15) * 64 + bit.band(self.data[enemy_offset + 24 * enemy_id + 11]:byte(), 252) / 4 local Att = bit.band(self.data[enemy_offset + 24 * enemy_id + 11]:byte(), 3) * 256 + self.data[enemy_offset + 24 * enemy_id + 12]:byte() local Def = self.data[enemy_offset + 24 * enemy_id + 13]:byte() * 4 + bit.band(self.data[enemy_offset + 24 * enemy_id + 14]:byte(), 192) / 64 local Un2s1 = bit.band(self.data[enemy_offset + 24 * enemy_id + 14]:byte(), 48) / 16 local Un2s2 = bit.band(self.data[enemy_offset + 24 * enemy_id + 14]:byte(), 12) / 4 local Un2s3 = bit.band(self.data[enemy_offset + 24 * enemy_id + 14]:byte(), 3) local Un2s4 = bit.band(self.data[enemy_offset + 24 * enemy_id + 15]:byte(), 192) / 64 local Un2s5 = bit.band(self.data[enemy_offset + 24 * enemy_id + 15]:byte(), 48) / 16 local Un2s6 = bit.band(self.data[enemy_offset + 24 * enemy_id + 15]:byte(), 12) / 4 local Un2s7 = bit.band(self.data[enemy_offset + 24 * enemy_id + 15]:byte(), 3) local Un2s8 = bit.band(self.data[enemy_offset + 24 * enemy_id + 16]:byte(), 192) / 64 local Un2s9 = bit.band(self.data[enemy_offset + 24 * enemy_id + 16]:byte(), 48) / 16 local Un2s10 = bit.band(self.data[enemy_offset + 24 * enemy_id + 16]:byte(), 12) / 4 local Un2s11 = bit.band(self.data[enemy_offset + 24 * enemy_id + 16]:byte(), 3) local Uf1 = false local Uf2 = false if bit.band(self.data[enemy_offset + 24 * enemy_id + 17]:byte(), 128) > 0 then Uf1 = true end if bit.band(self.data[enemy_offset + 24 * enemy_id + 17]:byte(), 64) > 0 then Uf2 = true end local Un2s12 = bit.band(self.data[enemy_offset + 24 * enemy_id + 17]:byte(), 48) / 16 + 1 -- +1, Lua thing )_) local Un2s13 = bit.band(self.data[enemy_offset + 24 * enemy_id + 17]:byte(), 12) / 4 + 1 local Un8s1 = bit.band(self.data[enemy_offset + 24 * enemy_id + 17]:byte(), 3) * 64 + bit.band(self.data[enemy_offset + 24 * enemy_id + 18]:byte(), 252) / 4 + 1 local Un8s2 = bit.band(self.data[enemy_offset + 24 * enemy_id + 18]:byte(), 3) * 64 + bit.band(self.data[enemy_offset + 24 * enemy_id + 19]:byte(), 252) / 4 + 1 local Un8s3 = bit.band(self.data[enemy_offset + 24 * enemy_id + 19]:byte(), 3) * 64 + bit.band(self.data[enemy_offset + 24 * enemy_id + 20]:byte(), 252) / 4 + 1 local Un8s4 = bit.band(self.data[enemy_offset + 24 * enemy_id + 20]:byte(), 3) * 64 + bit.band(self.data[enemy_offset + 24 * enemy_id + 21]:byte(), 252) / 4 + 1 local Un8s5 = bit.band(self.data[enemy_offset + 24 * enemy_id + 21]:byte(), 3) * 64 + bit.band(self.data[enemy_offset + 24 * enemy_id + 22]:byte(), 252) / 4 + 1 local Un8s6 = bit.band(self.data[enemy_offset + 24 * enemy_id + 22]:byte(), 3) * 64 + bit.band(self.data[enemy_offset + 24 * enemy_id + 23]:byte(), 252) / 4 + 1 self.enemies[enemy_id+1] = { --name = Iname:lower():gsub(Iname:lower():sub(1,1), Iname:sub(1,1):upper()), id = enemy_id+1, name = enemy_name, sprite_id = ModelBox, flip = {Fliplr, Flipud, horizontal = Fliplr, vertical = Flipud}, color_id = Color, hue = {chy, chb, chuer, yellow = chy, red = chb, blue = chuer }, dodge_ability = Un2s01, -- in (0-3) range number_of_actions = Un2s11, resist = {fire = Un2s1, -- in (0-3) range ice = Un2s2, lightning = Un2s3, wind = Un2s4, burst = Un2s5, slow = Un2s6, muddle = Un2s7, sleep = Un2s8, screen = Un2s9, desoul = Un2s10, darkness = Un7s0}, -- 0 = vulnerable, 32/96 = immune 1.2 strong_party_ai = Un2s12, -- 0 = use 1st attack type, 1 = loop through all attacks, 2= random1 , 3 = random2 weak_party_ai = Un2s13, -- same four as in strong two_attacks = Uf1, --random unknown_flag2 = Uf2, -- dark knight, tortolyde, killwave, kromeball, crystal ooze, brimstone, ragnarok, deathpaw, gargoyle, lancerot, mandagora, deathbringer, living armor, necromancer, troll (65) and down actions = {Un8s1, Un8s2, Un8s3, Un8s4, Un8s5, Un8s6}, -- IDs of possible attacks/actions exp = EXP, gold = Gold, --drop = self.items[Drop], -- unnecessary, but it auto-nils if there's no drop outside of datas table drop_id = Drop, drop_rate = RateV, drop_rate_text = RateT, drop_rate_val = Rate, regen = {type= Regen, value = RegenL}, max_hp = HP, max_mp = MP, base_agility = AG, base_attack = Att, base_defence = Def, } end end function ROM:get_bits(byt) local byte = byt local tmp = {} for i = 1, 8 do tmp[9-i] = bit.band(byte, 1) --print(bit.band(byte, 1)) byte = bit.rshift(byte,1) end return tmp end function ROM:get_bytes(bi) local bit = bi local byte = 0 for i = 1, 8 do byte = byte+2^bit[9-i] end return byte end function ROM:get_letter(Offset) --[[ 2bpp = 2 bytes per pixel planar given this: 78 00 84 00 84 00 84 00 84 00 84 00 84 00 84 00 84 00 84 00 78 00 00 00 00 00 00 00 we decode that to this: 01111000 00000000 10000100 00000000 10000100 00000000 10000100 00000000 10000100 00000000 10000100 00000000 10000100 00000000 10000100 00000000 10000100 00000000 10000100 00000000 01111000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 I have TWO planes which should be "merged" together to get a color now, seeing that the 2nd one is just a bunch of zeroes, it's not going to add anything to the sum so I need to read a byte, then convert it to bits then I read a second byte and also convert it to bits then I sum those up and display a pixel from colors_2bpp_planar[SUM_OF_BITS_VALUE] ]] local letter = {} local limit = 8 local width = 3 for i = 0, 13 do local bit1 = self:get_bits(self.data[Offset+i*2]:byte()) local bit2 = self:get_bits(self.data[Offset+i*2+1]:byte()) local bit3 = {} if bit1[1]+bit2[1] == 2 then bit2[1] = 0 bit2[9] = 0 bit1[9] = 1 limit = 9 end bit2[9] = bit2[9] or 0 bit1[9] = bit1[9] or 0 for j = 1, limit do if (bit1[j] or 0)>0 then width = math.max(j,width) end bit3[j] = bit1[j]+bit2[j] letter[#letter+1] = colors[bit3[j]+1] end end letter.limit = limit letter.width = width return letter end function ROM:generate_font1() local Offset = 987340+1 local length = glyphs:len() local imagedata = love.image.newImageData(10*length, 14) local glyph_offset = 1 for i = 1, glyphs:len() do local glyph = self:get_letter(Offset+(i-1)*28) for j=1, #glyph do local color = glyph[j] local r, g, b, a = color[1], color[2], color[3], color[4] imagedata:setPixel(glyph_offset-1,0,1,0,1,1) imagedata:setPixel(glyph_offset+math.fmod((j-1),glyph.limit), math.floor((j-1)/glyph.limit), r,g,b,a) end glyph_offset = glyph_offset+glyph.width+1+1 end imagedata:setPixel(glyph_offset-1,0,1,0,1,1) return love.graphics.newImageFont(imagedata, glyphs), imagedata end return ROM --[[ io.write("\n") for i = 1, #ROM.maps do for j = 1, #ROM.maps[i].enemy_table do io.write(ROM.maps[i].enemy_table[j].. "\t") end io.write(ROM.maps[i].name.."\n") end io.write("\n") for i = 1, 32 do for j = 1, 32 do if ROM.maps[1][j][i] == 192 then io.write("🟥") else io.write(" ") end end io.write("\n") end ]] --while true do end