From 8734df9feee79fa5f38c088b4e12f763db644437 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sun, 8 Feb 2026 16:40:35 -0800 Subject: [PATCH] Fetch character icons from SI files via HTTP Range requests Replace static webp character icons with runtime extraction from INFOMAIN.SI, extending the bitmap manifest to support multiple SI files. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- public/images/laura-selected.webp | Bin 918 -> 0 bytes public/images/laura.webp | Bin 1018 -> 0 bytes public/images/mama-selected.webp | Bin 964 -> 0 bytes public/images/mama.webp | Bin 1016 -> 0 bytes public/images/nick-selected.webp | Bin 876 -> 0 bytes public/images/nick.webp | Bin 986 -> 0 bytes public/images/papa-selected.webp | Bin 948 -> 0 bytes public/images/papa.webp | Bin 980 -> 0 bytes public/images/pepper-selected.webp | Bin 844 -> 0 bytes public/images/pepper.webp | Bin 946 -> 0 bytes scripts/generate-manifest.js | 58 +++++++++++++++++--------- src/lib/SaveEditorPage.svelte | 63 +++++++++++++++++++---------- 12 files changed, 81 insertions(+), 40 deletions(-) delete mode 100644 public/images/laura-selected.webp delete mode 100644 public/images/laura.webp delete mode 100644 public/images/mama-selected.webp delete mode 100644 public/images/mama.webp delete mode 100644 public/images/nick-selected.webp delete mode 100644 public/images/nick.webp delete mode 100644 public/images/papa-selected.webp delete mode 100644 public/images/papa.webp delete mode 100644 public/images/pepper-selected.webp delete mode 100644 public/images/pepper.webp diff --git a/public/images/laura-selected.webp b/public/images/laura-selected.webp deleted file mode 100644 index 70b5f8635defc741e5839ffdbc899aec7ea6b5fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 918 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 diff --git a/public/images/mama.webp b/public/images/mama.webp deleted file mode 100644 index 55c51bfba5ee7af364b1c2d6dc3e2f06e29b0989..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1016 zcmVJDZ_W|$Q)Nb0}F@AOagM9!$Vm%qXiai8BRed-ASUwql zUB7C*sB0AR+6{FVIq|?!SN}WVTYG=Se$E{vFlA<3qOgfk6!BHJoZdkPRQW7|Nb2c~ zv%}=7;Wlpz8!l_baJ=E90~;#{fhRZHsHc|z0RH)ww)YdqP^eLddYvc^ZVnv^x8xE_ zFoI7?R$GyNr);EOa62~9PuxaSI!?Is@u$#5p1}Is_(?Z>LtTYQ?%oD|6d6~+{Dl52 zfMM;1Djh;<|2$QM)OVPH*$POB5wteCKaS_js#uJKJiekXK3>n#KWe!^fmK`Dw+d@R z%R8fT3s<}=Q|LAR#FgegyK76;YUBPyv0HTgh)5sOpprLI#scaHpM#uzf#;gJ0a3!u z!o%<`Ni+STZxssUrRLu>|Hb+%H(c-!m$QfoOJr_nlOsEUR~wYh!2Mlk}Nzauvsp!=ucM}1gl5#B%_*1P!n#= z^Tx4cSk`ZpJCq2K=WZ~tED#N21{ZiMqG1t^ z-i>2V#`B>~nMw5hgTLT0b&ma+O`|wwA8OP4CG#Wq>%Z-fzA(o#`-8L0XHD+5QyL)_ zVh`uMtET6_)PgO6k&80_UI>bDJ}`5l4Zcq~iucX1GFTED!y`ddF(;KY6b@W82jzCH zsp`Vr$r#S8_tM5L?Jy_CkYR2@ubq0_ih1F>wuC>xUh@k#V&%cu#|t>IoU;YjeuVsR z^}n%*K0JdyJYv@8|MLSpRH}`k>awlVXAl03{v5DW`mx zwZHN<_nZ~+C7#j>D`otIM>f50UW_Jl+|wkdQ&fl!oxTxnfs>cWs(oftO&_F)&MxS; z>(0dJ#Wgi?yNBV+rj-)_%?irgdeW5x5@NiXp1+y-Q9P1@AGUU2>?R_l&gM(eX)J$G z9$>>8m9ZO}H}guNyBuUTw+8T3>}4$;`vzf@mc}wDnyp_7M?f&@G~Zvb1`Oq#0Hn4` mz{zv{ZRa5FH4lU$Rft7&v<03zYy+-cnU1C;$M6Xzh*w diff --git a/public/images/nick-selected.webp b/public/images/nick-selected.webp deleted file mode 100644 index d6e626c307de86107b1d8180fc56b39855ea7040..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 876 zcmV-y1C#txNk&Fw0{{S5MM6+kP&go10{{RJ5CEM4DklIg06uLlkVYgTp`k7~3ow8U ziEIF!IYCqYK>fP)J7UJeY~MV0_zzUO&>o4uupXK|%AQacarmO?uXr+n;0GI?0_Jbd zqwasW1kivE`ZieXbk3W{Z@UXxm0+IAI$^`vi8p#tJ^VI7Apkh_QkE z44OEeVj2aQgYIPr?$I!Ud3XwMMr z@-3Qb1GC5pD+F=l8$MgLMNvco5N(C{P;pmvtJT-OLSgxDBc?Q+i@EFhqgc=Kczf`K zv_4YLOBk<(oPyVz{s18!A|Lz4wBJuAwwLZedjGE}t3xZIU-9dA-woFjp(NPczg4&p zBbq9e0Pq^Z%Mr8ry=VX>`PQw$SG%&d>^6$pEBVaQ##f~#TN!o7V$}I%>h)y~&&FgK z6sd`}^);jhmf+Zd$5p4KJ##dIG%&f-8l#87b(hllO`Eoa-)bnAjZyG&vfMlP5R;D}e3S30Ux znu86a3T?Z2doxlz}k&w=U=QoJzgC95nZ!fjL&s;lGGf8_@GFiw}BfGclH>-CWRqU$KpZ#Kz7M3yw-QmY|4KxQ#IcI?*mWF@M)D&I{KRuvjnd#A#{1M``{ zX&~-kufWe(TN2z@Ww}wOai=T>j+I0!u5M_&{ZQ2_V&fW+46=y|B z($nqYEdhU~tl7tc`xmqIxhkYkPZMGnaRBmC)6}1qlLQx;%2B8_dcB!ObhFRw-|Kf$ zt44^q1E^L*U)_i}^*jgJfdhnubt}{h@8sk`vNre)7Lj(}e3;rbCZfK)?VF2Li8_-$ zB`Hfz0pYk0b92%2F1RFomK0l&9SFU0I-R=zfffv_iZYb`T)qoH3f8NC0oFXwbw%d~BYBnXjelhfg(o{(Qi}!fLtA$LZ056s&5WVjhJe$y zB)lNGyGKtWJ~3oR%c5Uq@KYts>6}Sq1Eu+a#Wq)?f5T$^0RsUGozT`irH9n2o2f{3 z6xcSY`}{80Gd+kd3POi=mM6F*GI-3F-LSV^B~K2E`<3C!u*-e@JV1A_xr<|>^090^ zA0Zhq%%C5h=FlH|w$#ukBvv)@z2BSA(qT&EFgKq`-SJrbjsZR4lU@eyb!o)*DvCQZ z(ehY?_c+pIeu)Cb{vJ2pFuOofH|%7DnW~gJk6tL0**a#hl~^GG{AU(O`Le`Lgtt|a z>!Hs$oz_>wLa}9oHoXk~AHcCdd4rjDIR@x+L(|m>L*TB?T>-RN#`X==+c3`B)G(PFjdwEE#U_~5QN-s#);S^AfN`meZL&sS#u z-#F}MdjsvMv4Ilm+P08qckd^OqQ!}v?Z!l*Ud4tf(h0(E)@!6PhK{?jDf%(>#u-=M z5VpjCcgM{Gui0auu>vX*y_XENS*TYM0B#w@S-)CrqIOw_GyviJv1D7<8>uiID zytwR<8V46C%0>kgv|g;v75g8LOi^-u=D+qs|Bd_KR^s!2ocA;{mT?M3<=^_}^uizA IF~wj20AD`xzyJUM diff --git a/public/images/papa-selected.webp b/public/images/papa-selected.webp deleted file mode 100644 index cc6b2213e83ab4bb63ba554049fdd71f55cab51c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 948 zcmV;l155l;Nk&Gj0{{S5MM6+kP&go<0{{SU5CEM4DklIg06uLjkwv5;p_;0!06+%B zwg66X z1GAb}WszNPzR16nzO)Nnb7KY!Xv-&v`g(GQo>0HOU9(ff+2$4cEeS%0Sp%AlMWuW$rG!wZq zF$gR_DBzjXYu~pn?-PEKg6k^+PCeacWHQ$Y3;SlT8x2J}U1v*0d9$nE|&vv3W^Lj^~{6jMD^~s9Ma9N-jm$a}5mF@(0=nuOJVfPJu4DnIP zRF_T*5UG|$mDBniaM$NSn2!bDckG|>-2=EkFYD{}k&jLB@1uvAo)hzB7RhTZfUV0K zq(98IAMdcvFU{)T=RUkcVZH#^Nm*w-HisDP?cUt1SNjIJBlKqJP)ufaM@ZjcOc$4R zKry}VMWc*IUx(+i;=$HG&|!i%v6w!ZSGJGmDCW1109lENsdNaiSr`-YC=^m)B zBCE|+2JuIaIb{&Z2QM*^!A2E=9pl=rJSSe&9yshAjk38IY54#5M_WJJ?R0k4DjuIw zI87*UR}>1EQ4lFT)({frUkFi1a)RijN-Z}1RCm14>Vazi W|0!}?kGe^x+}gj_&$m1aNB{u*cGB|z diff --git a/public/images/papa.webp b/public/images/papa.webp deleted file mode 100644 index 83935244cb85a6d520aef00a290e76be825c4c14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 980 zcmV;_11tPeNk&G@0{{S5MM6+kP&gpK0{{RJ5&)e6DklIg06uLjkwzpUp`kI>Y5;%@ ziEIF!GQD#T%6XAepS9}|FvGgAFiI3zRn(0m5jN~^hA)# zE_G{*BX zcnE#3Vqg9C}aGj^)+Wy=FlZ7;$&VL-d04%?jIrfB?I9+q4 zM5EI!|EDwPP~9TsIZ@@vVc;QaS8M>lBpjD0oYTlt-RT4lUZs!7{P1nXURyJ^tW z?G6rrVC>ukamnZY5674Am(+rW$h?STeEe)=l+k>)$mnb-uxh2Gca%&ql3M6GNM_Fm zE2dJgAx)>i0VJ@Tepxx%jy7r407)VzA(cuiyAmY_eQd@$WlhwN93sNw zCDC00R5R(I*&qL~_+=5QwV>&y1j@#$U$Kco%83TWzX}c}(|NF~0FW9cROmlz+>FOd>B%4W_zSGn zV-^1eN}quy`#@}K_ua&FHnc(iK89U4%CScbgn7p?%W~Rc9wfWFAvGDDQ~pxy;yXR7 z;hah+2#08rR+2EZzx27huaGN2Vv)qG7t{YF1?++aMc!5265bS>F8?hoe>kxmy>C;T zI!zRk<<;v53szn;FEBHe5EW)Ua(*wwF`VZMIEzC4mg<;Df^J56VmhxCm>RWiAOHYN CH{0g` diff --git a/public/images/pepper-selected.webp b/public/images/pepper-selected.webp deleted file mode 100644 index 1cacc1402e2a0f219218462bc9c6e6f90a5e19b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 844 zcmV-S1GD^6Nk&FQ0{{S5MM6+kP&gns0{{T94*;D3DklIg06uLhk42;+p%)fpKnBFN z08X5ssNWF2@8(5aYPu8dz;Y|hy?W0092<#_i|rV)S+=^Y~m6^yxpRnGqa8yW>gVPEw&8dliGJW1TzhotmtrMlA<& z*=kE4jA(boq7pn5pa3mM96)vcBmgTX(2*as?uyI_NmgMtCUw7kA#KiYobY-7a`u-L z>wi$49|TwbsIK-h{gX6awJ1{!HOQ={)M@KiMit;KiIw3$ium>nZuOE&mBb&VWB_tn z!4wZns(acD7IJ018sYIb=q|I{X=2i}hdHAP`Uqm$ETf|O;Ha;$(suM}61SsoM!ZWxE{49|m9RZV$UxX$_8OvNlkGp5ATVmeaP9+coW zaR)(=a-KZDVJ-=gxl~q$4}Dvt#3gtAY#7{=*iRkTRi6@qa90=duf+!%?lw>)qW2${BQ)DM`u&zlHtc?(5i}V#u z{_9>B968gsgM&Yyg?x*(>vQPfP9~4yrA||DOdMf#7emKWCC;R+i`NkAq8nqjs4%WE W$WSr6nvtmsg?QXDu>)HH@&EwzvzC4U diff --git a/public/images/pepper.webp b/public/images/pepper.webp deleted file mode 100644 index fdf9fa102d05805d9e3bde725a93067a1ee47623..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 946 zcmV;j15Nx=Nk&Gh0{{S5MM6+kP&go-0{{T<5CEM4DklIg06uLjk42;+p&0IE06+%B zwg66?psJq${>{ci%1;$>-qCs0^&hsk%$J;xNC!yQ><6$1><6ZQvS;lN?60&0PeTCg ztuQ4(_NV)|s5h1Gv2jAu$uXFA$p1lF0g>TPmSfrwSh59*9_PLAeC5Mj|C}n}pTv~+ z{X)=UfLcB(RzSK+I}`u_{D>WMD{eUS!nQz@I`GC4uG0OhsPhXAV|2C9`PewXD=~CR6Y5Sh};&jMWwEn|704hJ5%n_CN1W9mv zoB2uvzPEm(?hnNo3EVv_xg~e#gIde_E=mWt7IPtnOZ|2%06lf)vBbc*^Nx}VlQUn% z$}CY@a4myyleoePQfWMo5OzNcYQI+*DJ+YuvZ=a8gXkJWYi@U$$d>?*? zx+VXIseW}#{*Wf2*uDU}D-X{@3^$_YlHBonOYb_CZ(=l5b}QO+nMVMm&?NgcD?5S{ zA9bTLkww5j;oyiV_Em~Fup++!YdFGa;$G|Vzg<0E%oi750G%oFbg6XgvW%3?!y&5* zDjub<;?~CY_ZOQ|M^4+`(^@TK+zy2aq}Nm5X|T$Z-Oiwf-MX+7mhGj@+^v?J>fb~k z;7vLgqWkL0dOzLnDk!4o>J-`4tSe?i`NZDG*#fi;_UsexWAr|D++S~aH?KOM(ffVs zkrC%Q|HA|@^%KO7rTT1B<}Pb@t82t{1dg~~yCbx_-YHzIt7tUd0--O9Um0ne4Y*K5 z*J9^Pl=nAxi6cX6Nv25cCDhf+1B~X_0rs}1fF&5IJ+pmf2WT8lWdGRp!UpZ8GWrbb z`90>A$!Sup;v_@1Uu3#MqNcz$oq2szbFb`!{SaLar&mKm{`R)6M%JFG!^m`%`s!pN zpSpf{gp@FqwV2_fqG&>pzjb`oUiw9^>7r|Gl;M&}JZBvqkwt2!2PX=TU)pg=?%@X7 zDP0Lvty-hTG`&EtE0?m!FUgA9$rK objectId)); - const bmpRanges = findMxChByObjectId(isleSI, bmpObjectIds); + // --- Bitmaps (across SI files) --- + const bmpBySI = new Map(); + for (const entry of BITMAPS) { + const siFile = entry[1]; + if (!bmpBySI.has(siFile)) bmpBySI.set(siFile, []); + bmpBySI.get(siFile).push(entry); + } let bmpFound = 0; - for (const [name, objectId, size, expectedMd5] of BITMAPS) { - const result = verifyRanges(isleSI, bmpRanges.get(objectId), size, expectedMd5); - if (result) { - manifest.bitmaps[name] = formatResult('Scripts/Isle/ISLE.SI', result); - bmpFound++; - found++; - } else { - console.error(` FAILED: ${name} (objectId ${objectId})`); - failed++; + for (const [siFile, entries] of bmpBySI) { + const siBuf = await loadSI(siFile); + const objectIds = new Set(entries.map(([, , objectId]) => objectId)); + const bmpRanges = findMxChByObjectId(siBuf, objectIds); + + for (const [name, , objectId, size, expectedMd5] of entries) { + const result = verifyRanges(siBuf, bmpRanges.get(objectId), size, expectedMd5); + if (result) { + manifest.bitmaps[name] = formatResult(siFile, result); + bmpFound++; + found++; + } else { + console.error(` FAILED: ${name} (objectId ${objectId})`); + failed++; + } } } console.log(` ${bmpFound}/${BITMAPS.length} bitmaps found\n`); diff --git a/src/lib/SaveEditorPage.svelte b/src/lib/SaveEditorPage.svelte index 2d6b206..b1ff811 100644 --- a/src/lib/SaveEditorPage.svelte +++ b/src/lib/SaveEditorPage.svelte @@ -1,5 +1,5 @@