From 33131754d475845a4a8a5d2767b5ec9c9150d5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 31 Mar 2010 17:31:49 +0200 Subject: [PATCH 01/29] Prospector detects regionoffsets overflows. --- output/stones.py | 9 ++++----- tools/prospector.cpp | 21 ++++++++++++++++++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/output/stones.py b/output/stones.py index 5feb53210..de90f8e7e 100644 --- a/output/stones.py +++ b/output/stones.py @@ -1,12 +1,11 @@ # -*- coding: utf-8 -*- import pydfhack -x = pydfhack.API("Memory.xml") -y = pydfhack.MatglossVector() +DF = pydfhack.API("Memory.xml") -if x.Attach(): - success,stones = x.ReadStoneMatgloss() +if DF.Attach(): + success,stones = DF.ReadStoneMatgloss() if success: print "Dumping all stone" for matgloss in stones: print "ID %s, name %s" % (matgloss.id, matgloss.name) - x.Detach() + DF.Detach() diff --git a/tools/prospector.cpp b/tools/prospector.cpp index d07a7ae23..5cd8acfbf 100644 --- a/tools/prospector.cpp +++ b/tools/prospector.cpp @@ -13,6 +13,7 @@ #include #include #include +#include using namespace std; #include @@ -103,6 +104,8 @@ int main (int argc, const char* argv[]) int16_t tempvein [16][16]; vector veins; vector iceveins; + uint32_t maximum_regionoffset = 0; + uint32_t num_overflows = 0; // walk the map! for(uint32_t x = 0; x< x_max;x++) { @@ -129,9 +132,17 @@ int main (int argc, const char* argv[]) { for (uint32_t yy = 0; yy< 16;yy++) { + uint8_t test = designations[xx][yy].bits.biome; + if(test > maximum_regionoffset) + maximum_regionoffset = test; + if( test >= sizeof(regionoffsets)) + { + num_overflows++; + continue; + } tempvein[xx][yy] = layerassign - [regionoffsets[designations[xx][yy].bits.biome]] + [regionoffsets[test]] [designations[xx][yy].bits.geolayer_index]; } } @@ -182,6 +193,14 @@ int main (int argc, const char* argv[]) } } // print report + cout << "Maximal regionoffset seen: " << maximum_regionoffset << "."; + if(maximum_regionoffset >= sizeof(regionoffsets) ) + { + cout << " This is above the regionoffsets array size!" << endl; + cout << "Number of overflows: " << num_overflows; + } + cout << endl; + map::iterator p; for(p = materials.begin(); p != materials.end(); p++) { From 35960db5aadf8f154cc39a211e393231569e9859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 31 Mar 2010 18:16:18 +0200 Subject: [PATCH 02/29] Fix for out-of-bounds access error with regionoffsets. Breaks compatibility. --- library/DFHackAPI.cpp | 16 ++++++++-------- library/DFTypes.h | 2 +- shmserver/mod-maps.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/library/DFHackAPI.cpp b/library/DFHackAPI.cpp index fbd659f3c..07a53f32e 100644 --- a/library/DFHackAPI.cpp +++ b/library/DFHackAPI.cpp @@ -220,7 +220,7 @@ bool API::InitMap() /* * --> SHM initialization (if possible) <-- */ - g_pProcess->getModuleIndex("Maps",2,d->maps_module); + g_pProcess->getModuleIndex("Maps",3,d->maps_module); if(d->maps_module) { @@ -351,7 +351,7 @@ bool API::ReadTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffe uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->read (addr + d->tile_type_offset, 256 * sizeof (uint16_t), (uint8_t *) buffer); + g_pProcess->read (addr + d->tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); return true; } return false; @@ -413,7 +413,7 @@ bool API::ReadDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->read (addr + d->designation_offset, 256 * sizeof (uint32_t), (uint8_t *) buffer); + g_pProcess->read (addr + d->designation_offset, sizeof (designations40d), (uint8_t *) buffer); return true; } return false; @@ -426,7 +426,7 @@ bool API::ReadOccupancy (uint32_t x, uint32_t y, uint32_t z, occupancies40d *buf uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->read (addr + d->occupancy_offset, 256 * sizeof (uint32_t), (uint8_t *) buffer); + g_pProcess->read (addr + d->occupancy_offset, sizeof (occupancies40d), (uint8_t *) buffer); return true; } return false; @@ -439,7 +439,7 @@ bool API::WriteTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buff uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->write (addr + d->tile_type_offset, 256 * sizeof (uint16_t), (uint8_t *) buffer); + g_pProcess->write (addr + d->tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); return true; } return false; @@ -457,7 +457,7 @@ bool API::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->write (addr + d->designation_offset, 256 * sizeof (uint32_t), (uint8_t *) buffer); + g_pProcess->write (addr + d->designation_offset, sizeof (designations40d), (uint8_t *) buffer); return true; } return false; @@ -469,7 +469,7 @@ bool API::WriteOccupancy (uint32_t x, uint32_t y, uint32_t z, occupancies40d *bu uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->write (addr + d->occupancy_offset, 256 * sizeof (uint32_t), (uint8_t *) buffer); + g_pProcess->write (addr + d->occupancy_offset, sizeof (occupancies40d), (uint8_t *) buffer); return true; } return false; @@ -483,7 +483,7 @@ bool API::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40 uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->read (addr + d->biome_stuffs, 16 * sizeof (uint8_t), (uint8_t *) buffer); + g_pProcess->read (addr + d->biome_stuffs, sizeof (biome_indices40d), (uint8_t *) buffer); return true; } return false; diff --git a/library/DFTypes.h b/library/DFTypes.h index 5ef92670c..40b5c57cc 100644 --- a/library/DFTypes.h +++ b/library/DFTypes.h @@ -853,7 +853,7 @@ union t_blockflags typedef int16_t tiletypes40d [16][16]; typedef DFHack::t_designation designations40d [16][16]; typedef DFHack::t_occupancy occupancies40d [16][16]; -typedef uint8_t biome_indices40d [8]; +typedef uint8_t biome_indices40d [16]; typedef struct { diff --git a/shmserver/mod-maps.h b/shmserver/mod-maps.h index af6b8f48e..bd1ad94a9 100644 --- a/shmserver/mod-maps.h +++ b/shmserver/mod-maps.h @@ -33,7 +33,7 @@ namespace DFHack namespace Maps { -#define MAPS_VERSION 2 +#define MAPS_VERSION 3 typedef struct { uint32_t map_offset;// = d->offset_descriptor->getAddress ("map_data"); From d540f4cbd76d1838b44c46bea67c655a5ab9cbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 31 Mar 2010 18:59:03 +0200 Subject: [PATCH 03/29] Update precompiled libs --- precompiled/linux/libdfconnect.so | Bin 186846 -> 40483 bytes precompiled/windows/SDL.dll | Bin 32768 -> 34304 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/precompiled/linux/libdfconnect.so b/precompiled/linux/libdfconnect.so index 98aaad1d0786878cb82e0349073d851517bbb2dd..633f5c5e50b67b62d44d3586925e7e7e6895f444 100755 GIT binary patch literal 40483 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

+aes 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Νd1kcMGF$@f#LWkLqfdRC_N`EV;&Ac;(fk8-* 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 Xqz5Y0P>WpyP??aPidrauvJC?ObDi;Z diff --git a/precompiled/windows/SDL.dll b/precompiled/windows/SDL.dll index 61db5a270205e65ca74e9d4b5138dd01257a4ecf..4b9815255b1a08265c710bc77690a96e5ed3c474 100644 GIT binary patch delta 13836 zcmZo@U~1@Lnqb5DVxp~c{rnBDqi$bp<%Hq1dBU7GFSc@CS}DMJ3rw?dUIo))ocAxb za{dwj$9WH|zJ+rxSe%jbGME;Jsuuy%L7B-JATt@DAi$M@!H1ERA-Hz4H&lq>0Rxk; zA~z!^1A`v}1A_@fxFUG65~H5h9vwyoCI$z_hG{bx7z9940#F)43Dhw#M6oh3d?}e+ z#i+z|gMIQrCS}I#$%h!t%x?KIKoo(DWdZ3$UCs7|JGxF{v|FOs->+;tSwpU|`t5z`(%8z`#(mc^Oj_ zPD)?(4*pBcd*kg^bDQ$S$Y!V{B|Sc(Myi*h6~2z+x<;b1J`412+A zH<^_+re0$rgFv^3M0bEdU=S+<3j;&IiygKM49!P)4xc%5=FCEfyg;{LXMo6I1fPF9 zL;xnwzYT>i5!gEcWLUR@Kv2MobcBI0_1ys?oq~tYoT-Niq;&^~q;(1&hVi=sL^?nU zx<$b*a1!YD5b5+1X|9kEC}Qn)V0q2le1OFTKj-VPNRyeKUbUpxgAtWPNU>`WGN}pbR*eI>B)gcevY61`@2D zU7&D@Lj(sX5F!zlH@{I?cwz#Bfbo$|7Zruz(6DeQH=x%=#Uf=fD1mjGf~;;l1~RGF zjU}MhM@8nCi;6VEf6<%-1}*`JbF_J1Ouof!qzZCQZ-|OSFNT*oh~XaX#R+S`(v^wuMs=gUfvgz{di5KLALk0sMs){4G4rf))Xc` zkJk{c_8PApoGs1g$@pS&DxVizWE-C;qsHVnd`=SYj6v=Ufh6*Sj|G_TcSeYG^J+|X z<5yu+nVik9DRDv*q=cpAKnaWS?bqtvyeyyy6xEo#hF?_^NSfle0{ zk!~Lqkxm~Kk+kj*6){9u|KitVd@)%?AdXREa2HP8x_X)@_ zeNvlzN8koijN#^Of&z@f7mx#tBN!CflWz*eGyT$^Y$fa?`~i#fOyO8(8+`_bZr?8{ zoxGsr5E&mA-TgEc92HsrGbEyrQZh6SK#7M38t5OhSYEK}Pqq~4XY$aSd`e^{Kbnro zMWX6V<+_t+i=Gw;Kp58irexuX&1GWcj7-Yfli!QS^BvLx2V53I7Q>4hT9acWb~4Fo zO%|6dQJ<*Ez|j3P_K*MrILkwbtp6EP5XONE$xV`l{5#RajHCiZULeGp-$+1G&*te; zK8*4b(Qw~2zp;RNuhT`v1fpm%lgw2nSGCPIW#kx{BvmK>lzk-_g3uBO3F0qu^+KHp z(dIWC;E->XpUCJiSzN(Ra)B6wKt@LrgFqHj7F+kZ7vV4e{}0b%$w-)-rywqQK!QOa zBLgDK(S7cPI#kwS@;n7`$pk?Lfs6o%ELZor7vEn%^cGA$rywrb0Ft$U$ntcbdvP2p zJ7Mxa1#!s@AXx>7EMNDz7t^7#2PW$&ic4Jp$#Ots1-j3@NQcTk5S$#ZsKuzTxl1vU zQRpDTV}Z~>`KlBkaatZ6S;hxC-FQ0P1UlVBy3N8~Y?YrJtL!QK5uvB~4G$!cb}HvG z@yJisQ1KI1j6n)?sC1`FE|ZPi<{v8Bi~=4A`9N?;8mn20Es_CQDALUq&{@aRStrn0 zC-S0CW^#*K2nQ%bhj-h)nEXIZ2a;!PUrZKO*Oma~{%#+Ai@^V)b3o-3Bi~V ztPRrDZ3|W+duIZJfc3vJw*R72Kmie-#AI&mMy3hkoBOm|8SBp>Yp;QXB)DJ> z=xtE}wGklssnf3J^)+al8ergAt_U{J;PId+U+()PwYdsDuQ)Fy+M518S?hm;=(| zqY?tM#^=rd|J}CWATtFA*?H}AJ32c$I=;VTzIgB#YxkjpFXWAHGauq_0o5tiH;ROy zVF}d)uEu&<^gtz^_CJW$_srKpT2CE(DbIY$`eqRiR14UK<{CW-fg-l<535L0~BXadvG~!a)COl@ezUMqc+wz_?wFV z|Nr0J0||C`sTUjFc%*}YfdO3db-Sprgn{bn6S@}QCRS&lKz9I7Hy5%RQ|KZ7G>a+R23ZUNoo*H{B7{L{O&|+Yh-tk3|38Z%izQ=$6@x&=i)3)g$k2W6#rqpji3=c! zBM=Fu?sG4$!6Y77GYDkNfJm@(pL?+vCZPb5D1b;>aLAiyAyVF8h0?mqXT z8%2TzBEjB$?nTj!$uc)CY%l=covTb(g3}bi0)>9|bjA!x8P)?iv-5Zdp)k zHLE@#{Qu=+?)41aZWYW&n~(5-lp<7S)d%#pgJL(}|K(%t|3T@ayR4@92oLku#^a#a z-N^%v`uMomXmH8{*KXlwKuHCZ04FjCghShA6)ZayA!?5?Fe114Kw7f?XP~!pp~Z`f ziU*`@$&g8z%*HNP|Kj5R{~0fe!A)C8(^n*gLEy!70R{$$wE^89UmU*t|Nn6|kVN;f zxI-+knz#84M{suxC}ci%y9tDM*YR|p&^~|gCzJK*a@H4d|NsBD{sAvX%|g51b=Ihe zw00!2*!uXP`fkBe?T!f}X&q1%<=^$U={1HnnS+d%{5ES_#}7Uu321_p-E zu;#-o;Q=pvK{?%`TNjk6I`@LIT6euOOE-^2<53WY`FN)j7>w?MZDe`lyjx3fqv^YMUQUx~oZ8Wour$N&ES-yN(G%zQnlx7CLc z)alv+67r4-WuPF-y!%j(Xe5Wn#E)HS-(7SgINQ@sW)?Mtuycv|Px_#ILw0+ok zm_Ho+$<%y=$M}Co?+q3PhL)3M3BA2fSQr>OeN-gEyU)FF{`dcXw*Y_rHl}VL6^XP? z#~&b3J&5QwCl-h(BS=sZBDmd)quWJAqSNCK|F)?hL*1JX@N~MUNLc?WS_f*tc6-Zo ziEwmG-NMYk(Cux~ZNky|twi)#Yn%`R17mlvMHi1)M{m3k1H)@}h#*6Eb$tZ$ZivI# z1+;zGd6<73{K?eOdq;qQ;kb*61Sp5JoGf$c-Fi=ef#EfCuO~xyh>Ao|_xTqspzvhq z=IG?>4q)jFW9fF_=yU_SkbfIkzeDpei1Q8>od!EEI3cZ@MjWVNhS=yxP_73j;^sFZ!QBxW&A|88FnW)A-CjMgqHB1LM}^)4y`#R~k}9ht0MR3wVTtX))OiUs($JF-~2 zs3;V%S-YqR6iGT;-!Iw^c2<1caR-h6|NsAwjcz_7au`zOg?2`1ylDReZl-{WE|3f; zJHjxwS^`=gO}5rosjq$X|9{4Y6b6CrbH`m&WI&OA+(m^4RAK%XElB{iml+rsj=RY~ zL_jr32B>m|Rwj#hAe9NII4^nvtusE@G6-a}q`)f^%S$i`M^GCYBEi^w?uEi7NvMp0 zJ*aAc$bhOD_Dhp(3?yZnZP!d-5MU{(1yukX%{E)VOkfaTDE04V*(x$|a;#i?RfjNt@)EVq727!S8q8tef0<8y1g#Yt`TyPxX0#M+_9ftaX zu~YQsc1j7Z2Y4|G&@zG`M8TlK?XOy79N}K9PwG0)hXpvvf~m znK*f_VKfuV#L2%5HHBFwf(C9_0wGQ?IuLjG%;s%IyBOL3tFD;HAaG{#ag$1p#y2c1 z3=9iTOs+67W;`)@jfvXiAXB~i|Ef1YO5nlpU-b?e{{b5R2^#+e8vhL%{{tHT3o_s2 z%)%cNLBUoklW`{%)U;FprN0ZQAWw_RBrpgp2Fd?_0C!-b)?)RGfB*mgU-b{N zVIVcIP-K06|7EIv$(%GT{s;C-l z4JI-OFz~m&Vq{}qo8tXJwnY_bH)@DZg#BCJUO2se+P2Z;J{jYXoF5 zW-ivUAdScW_UKd3%c0Yw?8!nZzFqTFc!DX=<=Siq%O zX8}hyxTV`!$n#?QTgm_bU&}xZ`;y8a5C-w|aaNVd3KmMLi1g6?q4is-J~YU#WWj?h z<4V?KeQVLlH5R<}N3uW$T+i47q8CSie0Du!37F#m;Yck zM*+;?n8YCP|9XZ1m;*}s|F37TfH@@)WnVHteyD+PUVu3VAeM*_^L2bFaHuPlq>G3<8TcOxClOR{}*q_y5rDH{V=2n8Le%mZ*khltnNI^p^km zZGR>2zLWP@%ku=3FbKSGU}IqTZyGZBxwW#m02>2CWOM2t zkib+1f&bS!U7!3n4VbKGqab~mm4Ttt_0Ip&C;zW44gtxQKKOsF`H0BkfXOj7(u~t5 zSJ)^qpE+}8@+=!EU66vs0aFV~k@m~}q{GXL)(&V?c zDvTbJh3qtpLB=%yU@GCwl7GPqGWq3JQ2*Ez@j`4*_J;QFNb zA7iOX>w!{*@PObK$51jSv(m#Kx*k%~oQ@ko*N|pgkvHqC^ zl2iQwrgXndVi1_q&#Ez*L7>}}VX+LDW?8H-nL(h}m1D6Eh~N6)&;S3&Su-YqnthB6 z3l%0a)C(+@nFLBh0+Sd7I9V7D9A^~)OM{v^puldu@#p{lg=fIbJAeNF|1atSs-Y4l zF$iQaz6fS!V2I3O_%G@KYIXd`0#%1TplL8$kOGEMjx5F(`XDtKAhZ9AN`R{Q|Dpj2 z3<4()|5sg;pvxfeUv&d05vlF~8F4n0K_KP7DFXuo1E>$wnWG}mo1-G2{Qxo$>+IyC z#o*5%kO9iqFQ$S=4JBd^XIM;S5O~qU#J~`Fxch(Dj*MIefqfadlkYo8bA$K=AbtUe zzfh^|H@VkUkI5%*@?KXN8R6}qJ_g6(?hqA@ zUZ*eJULTB4yx8~k|Nk8u@+N1cuH_}oCF0c!^?(~ll43mnOKS^ z2YQM{xq?J{YySWL|I!M?^xg5&=p@uPO(mdVpRR)c|Nk4Gcp(ikYeq4Hz)NnB8^J^B zFQ*-Vs%7f-`eS_J#kY_D|L-^gQuJ){Mo;DX6DQ!|26fUlkP4j=27#B$K0>?-aybH~kgZcypffquMX@G7Y6_ym}Fg0Xy9O4DFEC2sbKFFk2-%!TD&{4*~Ft3b( zVO<#m!=5q*hD&7(3~$O97#PYK7=+3h7?jEx7>vpp7(B`u7$V9U7#`FzFkGl*VAxj6 zz%Z+pfuXIIfgz)ofx)Mifx)7dfkCR4fq|u#f#F*%s32irm{88ZpjE-Z&``m^aHfKR zL7|d?p{SC9;Y}q&J;Q@?h$4Y%1_rfi1_qC628J(H3=C=23=9R;3=9?33=B=x3=BQh z3=E5^85q`7GcfF`W?(o|&A{-gihOc@LAr`U(1jK&p4`C-IrYI!lFbKT! zV-Prz%^+|K5_2+B6yQcF6qhCyCl_TFlrXrbmMA0_ zr6!h?7NsgAmMCQArKDCcFo4DLQY(-oGxHP@6_WBRbQFqGOG=CK6!KEbA<7lw8mO}K zn0%1QIucY~GvqP|fQBVO!3V<}6Bq1_qEmmB|Og#ZxyxRf1$dDi0v(28qG2!vqF_J4k9lw!O(_5cq+l9;60j21pOG zZFeR+h6~qg zl7S&Il7V4SBm={RNCt)vkqitHQ49{-PKaO-P-2}d335+GE`z{C zMUlx35geugwvfQwlFJ|f3d|$93<3{w83g|1Lc~D<3Gx@n3=plsz`&p|d2fU?W5DE_ z5z-Pe{tTdbrT+(z%$LZ6nC}q7$&V!L0u^qE;E-hyfEeG92Z^ViJcN}TlOIM1OC*Ru z3|5FlID7@v1cgWrfdC}&D^T$XahxKN5QAqzrNNOl`C_#6u6Y3I>nl%NGfCq)o2BF$geD{unC`iXNAp2AHNq6d`nhOc97d@L{IZfJ|Xv zV8|$95SV-+Qj{kK;o%*VFUEmWh{hf>)F@nS3Q+}$!hm8(lxP$qMd6ns%qVD3a&j+;f4qfA6VA?Q4Dbb zM+s`y1(jJaD?v0U>o(Xy0!ASg;w_MwDkTuB8X{OGe~6P{I^sW>D_%|@Hy9Gmmca}H zzQGIvlRw0X)b9&s5O@xi0QKxZ=3e>#A3QS(u1H%S)28Idl4D}2OZVU_;+(2z128IdV3=9q)3=9Dt3=9PzOWYV37PvDoFnBUB zEC7jlFfcTDFfdH;U|&i$QAL7#Kc)90+oRI|IW5 zkfS{q7(5yn7(yEu7*ZPQ85oKi7#JEF7#JouFfc4?U|`tPz`$_4fq~&c0|UbcP^dIA zFbFp?FsL^&FjzJ+FgQ0dFa$I*Fhn*oFl06|Fw{0OFidP@U|7<~z_78Af#F~y1H-vS z28R2M3=E$e85sUFGBEHqF)+w9F)-*fF))}nF)+9`F);WwF)+k}7+FmW4CPG>3@uFz z3{#pI7#24%Fl=aIU^vjkz;L#Sf#G2j1H-2#1_s7v1_q&K1_qU81_q;M1_qaA28Q5f z28Oui&1G3yERza27(YzrERdG{kOj$cdiIWCsTIko1)z4YLvcxpg9F2d$&Ll`j2|W^ z7N~0?tIIDfiO)}qFG|cyPlYOFa7bhj098XDHun~UGj8T6`ommrz{tSBWWZq{Y9MV8 zY>;SBWiY{Dn!yQ!a|YK8z8XjyIvOS#78*7hPBh$Tc+l{o;UhyvBVHp#BSRxcqhO;X zqXwg?Mzf6$8(lJbV)V|4(^$+{-`Lssj`2g|cg8Lzo+e=?StbQ0%Swg|UUD zg{wunMXAMPi^CR|EFM~XvEZI{MI?uY*y4!k!^<3-K)*G!aTi4&T{%Or@<75+K zlVsCj(`Pf+W`)fboBcMYZP;x;+b*-)VaH*gW?x}{%>J`IsBOf+u!VtvfzcqLm4ACqfnz@(`M6&rmIZ%o1QUcHWM^+F-tZpGuvhM$&Ah1%e>iqviV{2OXd&F z|C{q$XjoWToTf+3Jmzv~{p`igmyBV(b0Z7pxy!f3yB=&2OV@V`^h% z<7*RSlWLP?Q)|;?Gu396&03pHHb-qv+1#~xWb@VLmkqD2kgck%maVO=lWmx7rfr_B zjNM_oD|Vvxe)etlGwc}*7#NO#7Bv}U7>F1$8wnZhFxqeQ%;=5LC!=}^V>x3@V|`;& z<7DGZ<9y>%<9g#(W>wRwUD)vwV!pGb))rq>s{8ztRGqn+bG+F*yPw$+sw6DWV6j? zx6OH*D>g4|-r6wQve`=6%GcXQ*v8qW+fKFpZmVo(Wanb%XP0SLX4h|b!tS=+KRXV4 zBl~dskD#D;U|>jKVqo}a*lsk(=&;dcBNk&BV?E{7W=Up6 zW=&?3%odq#GCO2;$?TEYCo?8iBJ(EmN#={pH<=$d|6u;xT*<=7 z!pS1YBFUo2qRC>C#UhJM7KiFBE?V5Ocwq6=;*G^e3r0(JOF>I{OCw7M%OJ~W%S_91 z%QnmDmUAtaS#GvGVR_N=wdGgKUzUtk>{dcn3RbFCx>m+ko>uWz#f$i zt&dt?vA${j(E7RcJL@mjzpWWY^P;sU}tV;Yv*F;Wfy1{ zZWm{lVwY`KXjfrZYu9YoYd6kI5V z?C069wtr^-9hArd7#IRTjV1$j10Dk<13d#DgAju_gG_^ZgARiQ20IMS8eB5CVer}D zhXISBh@q6BqM?PMongFTieaf?ui;X|)rR{G9~gc#{A0*sq;F(qWNqYNUb! zF?nh7-sGnVlPRyMq^X>#o~f~^rK!DXsA-I8ifNWw|vpi_|$dbuQ%PQZh)oPj5R;$xizpTuyU9Hos zXIpQzzHH5GV{8*;GvDT!%|{z*Ube61_m8N z3&Q}z5W@(=7{dg^6vGU|9K!-bfqD}W6A6SpBiGv39Tyv5v6Lu+FhAu&%J4 zU_HfpjrAVu1J+lpZ&<&ueq;T?`iHfEje?Dejfstgjg5_ijfYKwO@U2`O^Z#3O^?k4 zn;AA6Y!28QvAJS%!{&j_51Sg>4%-#B8*F#j?y)^!d&2gP?GxJ{whVS6b~1JvcJ&^1 zA$BEpHFhm_9d;A!me{Sa+hKRZ?ui|XJ&(PFy@P#( zz%Ye@fkDPV$H2nC!63z;!eD~I3WE&>I}DB(JTdrSAYdqCXk+MMm|<98SYtTHaE0Ls z!wZIY3|WjMj5LgFj9iRTj4F&ejOG|ENjBPJbi(L^5rZ+0v52vSv5#?zagA|{agXs7 z;|0bCjL#UqG5%uw$C$x{#e~O1!9>Hv#3aL{!(@WV6q7Y38%!>kyfOIzYTr#@U;r=F aG2k#zF>o+QFlaDnG3Wq0Yja|)1``0ybd@at delta 11459 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 Vg9d{ZgC2v;51ZwgCLdtx1psDw4W$48 From 8137c73b983d3c9e9bbb7bc6309c42a6ce5203a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 2 Apr 2010 15:47:08 +0200 Subject: [PATCH 04/29] Beginning DF2010 support --- examples/CMakeLists.txt | 4 +- examples/creaturedump.cpp | 2 +- examples/veccheck.cpp | 69 +++++++++++++++++++++++-------- examples/veinlook.cpp | 6 ++- output/Memory.xml | 86 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 22 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 71a8d6bb8..2e3be5a4b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -54,8 +54,8 @@ ADD_EXECUTABLE(dfsettlementdump settlementdump.cpp) TARGET_LINK_LIBRARIES(dfsettlementdump dfhack) # veccheck - read vector values at address -#ADD_EXECUTABLE(dfvecc veccheck.cpp) -#TARGET_LINK_LIBRARIES(dfvecc dfhack) +ADD_EXECUTABLE(dfvecc veccheck.cpp) +TARGET_LINK_LIBRARIES(dfvecc dfhack) # catsplosion - Makes every cat pregnant, and almost due... # Author: Zhentar diff --git a/examples/creaturedump.cpp b/examples/creaturedump.cpp index ebeaccd3c..125f802dc 100644 --- a/examples/creaturedump.cpp +++ b/examples/creaturedump.cpp @@ -355,7 +355,7 @@ int main (void) { DFHack::t_creature temp; DF.ReadCreature(i,temp); - if(string(creaturestypes[temp.type].id) == "DWARF") + //if(string(creaturestypes[temp.type].id) == "DWARF") { cout << "index " << i << " "; printCreature(DF,temp); diff --git a/examples/veccheck.cpp b/examples/veccheck.cpp index 06244e793..66c6431c5 100644 --- a/examples/veccheck.cpp +++ b/examples/veccheck.cpp @@ -12,6 +12,31 @@ using namespace std; #include #include #include +#include + +void DumpObjStr0Vector (const char * name, DFHack::Process *p, uint32_t addr) +{ + cout << "----==== " << name << " ====----" << endl; + DFHack::DfVector vect(p,addr,4); + for(int i = 0; i < vect.getSize();i++) + { + uint32_t addr = *(uint32_t *) vect[i]; + cout << p->readSTLString(addr) << endl; + } + cout << endl; +} + +void DumpDWordVector (const char * name, DFHack::Process *p, uint32_t addr) +{ + cout << "----==== " << name << " ====----" << endl; + DFHack::DfVector vect(p,addr,4); + for(int i = 0; i < vect.getSize();i++) + { + uint32_t number = *(uint32_t *) vect[i]; + cout << number << endl; + } + cout << endl; +} int main (int numargs, const char ** args) { @@ -37,23 +62,33 @@ int main (int numargs, const char ** args) DFHack::Process* p = DF.getProcess(); DFHack::memory_info* mem = DF.getMemoryInfo(); - const vector * names = mem->getClassIDMapping(); - for(int i = 0; i < names->size();i++) - { - cout << i << " " << names->at(i) << endl; - } - /* - #ifdef LINUX_BUILD - cout << "start 0x" << hex << p->readDWord(addr+0x0) << endl; - cout << "end 0x" << hex << p->readDWord(addr+0x4) << endl; - cout << "cap 0x" << hex << p->readDWord(addr+0x8) << endl; - #else - cout << "start 0x" << hex << p->readDWord(addr+0x4) << endl; - cout << "end 0x" << hex << p->readDWord(addr+0x8) << endl; - cout << "cap 0x" << hex << p->readDWord(addr+0xC) << endl; - #endif - */ - + //const vector * names = mem->getClassIDMapping(); + + DumpObjStr0Vector("Inorganics",p,0x16afd04); + + DumpObjStr0Vector("Organics - all",p,0x16afd1C); + + DumpObjStr0Vector("Organics - filtered",p,0x16afd34); + + DumpDWordVector("Some weird numbers",p,0x16afd4C); + + DumpObjStr0Vector("Trees/wood",p,0x16afd64); + + DumpDWordVector("More weird numbers",p,0x16afd7C); + + DumpObjStr0Vector("WTF",p,0x16afd7C + 0x18 ); + + DumpObjStr0Vector("WTF2",p,0x16afd7C + 0x18 + 0x18); + + DumpObjStr0Vector("WTF3",p,0x16afd7C + 0x18 + 0x18 + 0x18 ); + + DumpObjStr0Vector("WTF4",p,0x16afd7C + 0x18 + 0x18 + 0x18 + 0x18); + + DumpObjStr0Vector("WTF5",p,0x16afd7C + 0x18 + 0x18 + 0x18 + 0x18 + 0x18); + + DumpObjStr0Vector("Creature types",p,0x16afd7C + 0x18 + 0x18 + 0x18 + 0x18 + 0x18 + 0x18); + + #ifndef LINUX_BUILD cout << "Done. Press any key to continue" << endl; cin.ignore(); diff --git a/examples/veinlook.cpp b/examples/veinlook.cpp index c39dc2b65..32980fa40 100644 --- a/examples/veinlook.cpp +++ b/examples/veinlook.cpp @@ -507,7 +507,7 @@ main(int argc, char *argv[]) { int color = COLOR_BLACK; color = pickColor(Block->tiletypes[x][y]); - if(!Block->designation[x][y].bits.hidden) + //if(!Block->designation[x][y].bits.hidden) /*{ puttile(x+(i+1)*16,y+(j+1)*16,Block->tiletypes[x][y], color); } @@ -524,7 +524,7 @@ main(int argc, char *argv[]) { for(uint zz = 0; zz < effects.size();zz++) { - if(effects[zz].z == cursorZ && !effects[zz].isHidden) + if(effects[zz].z == cursorZ /*&& !effects[zz].isHidden*/) { // block coords to tile coords uint16_t x = effects[zz].x - (cursorX * 16); @@ -564,8 +564,10 @@ main(int argc, char *argv[]) { if(tileTypeTable[blocks[1][1].tiletypes[k][j]].m != VEIN) continue; + /* if(blocks[1][1].designation[k][j].bits.hidden) continue; + */ // and the bit array with a one-bit mask, check if the bit is set bool set = !!(((1 << k) & veinVector[realvein].assignment[j]) >> k); if(set) diff --git a/output/Memory.xml b/output/Memory.xml index 6675cceb7..abcef55fd 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -1986,5 +1986,91 @@

0x09048d18
+ + 851c1190b6a7b42f2463967623d18575 + 0x4BB45F99 + + + 0x0 + 0xC + + +
0xe32798
+
0xe60838
+
0xe60814
+
0xae82cc
+ +
0x17f5ab8
+ Found addresses: (next to each other!) + 0x17f5ab8 + 0x17f5ac0 + 0x17f5ac8 + 0x17f5ad0 + +
0x146e45f
+ Found addresses: + 0x146e45f + 0x185b677 + +
0xae82cc bogus
+
0xae82cc bogus
+
0xae82cc bogus
+ + + +
0x016ad738
+
0x016ad73C
+
0x016ad740
+ +
0x016ad744
+
0x016ad748
+
0x016ad74C
+ +
0x016ad750
+
0x016ad754
+
0x016ad758
+ +
0x016AD718
+ 0x10 + 0x0092 + 0x029C + + +
From c503808a822cf3f1d5ab7d994f0402ac69ead642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 2 Apr 2010 15:59:17 +0200 Subject: [PATCH 05/29] Offsets from belal --- output/Memory.xml | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/output/Memory.xml b/output/Memory.xml index abcef55fd..8cc7534d5 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -2012,9 +2012,11 @@ 0x146e45f 0x185b677 -
0xae82cc bogus
-
0xae82cc bogus
-
0xae82cc bogus
+ Bogus:
0xae82cc
+ +
0x017f6f38
+ + Bogus:
0xae82cc
@@ -2035,10 +2037,44 @@ 0x0092 0x029C + + + 0x18 0x0 0xC + 0x1C
0xe32798
@@ -2036,6 +2038,21 @@ 0x10 0x0092 0x029C + + + 0x0 + 0x1C + 0x38 + + + 0x0 + 0x90 + 0xF8 + 0xFC + 0x110 + 0x114 + 0x6D0 + 0x770 + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100 + 101 + 102 + 103 + 104 + 105 + 106 + 107 + 108 + 109 + 110 + 111 + 112 + 113 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100 + 101 + 102 + 103 + 104 + 105 + 106 + 107 + 108 + 109 + 110 + 111 + 112 + 113 + 114 + 115 + 116 + 117 + 118 + 119 + 120 + 121 + 122 + 123 + 124 + 125 + 126 + 127 + 128 + 129 + 130 + 131 + 132 + 133 + 134 + 135 + 136 + 137 + 138 + 139 + 140 + 141 + 142 + 143 + 144 + 145 + 146 + 147 + 148 + 149 + 150 + 151 + 152 + 153 + 154 + 155 + 156 + 157 + 158 + 159 + 160 + 161 + 162 + 163 + 164 + 165 + 166 + 167 + 168 + 169 + 170 + 171 + 172 + 173 + 174 + 175 + 176 + 177 + 178 + 179 + 180 + 181 + 182 + 183 + 184 + 185 + 186 + 187 + 188 + 189 + 190 + 191 + 192 + 193 + 194 + 195 + 196 + 197 + 198 + 199 + 200 + 201 + 202 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + 66 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 0 + 66 + 11 + 10 + 13 + 12 + 14 + 15 + 16 + 20 + 38 + 19 + 41 + 39 + 43 + 42 + 40 + 44 + 45 + 48 + 27 + 49 + 21 + 47 + 26 + 46 + 50 + 23 + 18 + 30 + 32 + 71 + 33 + 34 + 70 + 31 + 28 + 69 + 25 + 22 + 29 + 72 + 35 + 36 + 37 + 67 + 73 + 64 + 65 + 17 + 8 + 2 + 6 + 4 + 1 + 5 + 7 + 9 + 3 + 24 + 51 + 52 + 53 + 54 + 55 + 57 + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .,:rsr, + :2;,;r2A@@5 + @2::s5A#@@@ @r. . + sd;:riXA#@@ :@@@Gir;;AS9 + Bs::sS3A#@2 @@#AhXirsS#; + iHrLr5d#@@@ .@#95sr;;rie + i*' `*@3 @@A2sr;:;r#5 + :..:rll: @@A5sr::r3@ + @Hr;iZ#@@@@ `:rr;;;;: + S@r.;i2#@@@ @s. .. + @2::ri2A@@# B@G2ir:...5i + :@r,r3X##@@ @G5sr:..,:A + .@Ar;;rSB@@# H#2sr;,..,is + .' `* ,@ASs;:..,:B + ;rr;:,..,:. + `''' + W I N D O W S + and + W I N E + + + 0x47b6fac2 + 48c831b6f3950913b8e1aeada563d2db + 0x0 + 0x4 +
0x014639f4
+
0x01463288
+
0x01463388
+
0x00c7bc00
+
0x01287d14
+
0x00941288
+
0x01287a9c
+
0x01463358
+
0x014a6994
+
0x014a4eac
+
0x014a6834
+
0xffffffff
+
0x8b010127
+
0x014a4edc
+
0x014a4ee0
+
0x014a4ee4
+
0x014a6680
+
0x014a671c
+
0x014a69b4
+
0x01467b78
+
0x01295d58
+
0x01284184
+
0x00941288
+
0x00c2358c
+
0x00c68350
+
0x014a64dc
+
0x014a4ec4
+
0x014a4ec8
+
0x014a4ecc
+ + 0x08 + + 0x08 + 0x2C + 0x0062 + 0x0264 + 0x0664 + 0x1D64 + + 0x0 + 0x1C + 0x38 + 0x1C + + 0x00 + 0x6c + 0x88 + 0x8C + 0x94 + 0xE4 + 0xE8 + 0xF2 + 0xF4 + 0x158 + 0x1F8 + 0x1FC + 0x228 + 0x2F8 + 0x2AC + 0x328 + 0x32C + 0x330 + 0x3CC + 0x438 + 0x43C + 0x440 + 0x444 + 0x454 + 0x474 + 0x520 + 0x5D0 + + 0x70 + + 0x84 + 0x86 + 0x684 + 0x6B4 + + 0x5C + 0x58 + + 0x4 + + 0x10 + + 0x84 + 0x60 + 0x08 + 0x2C + 0x0062 + 0x0264 + 0x0664 + 0x1D64 + 0x3C + 0x2 + 0x4 + 0x8 + 0x24 + 0x1C + 0x20 + 0x00 + 0x7a + 0xD8 + 0x68 + 0x24 + 0x1C + 0x28 + 0x24 + 0x24 + 0x24 + 0x5C + 0x94 + 0xB0 +
+ + 0x47c12f36 + 8bd90fb6db8388f129fde224a35459de +
0x01463bec
+
0x01463480
+
0x01463580
+
0x00c7bc04
+
0x01287d14
+
0x0094128c
+
0x01287a9c
+
0x01463550
+
0x014a6c60
+
0x014a50a4
+
0x014a6b00
+
0xffffffff
+
0x8b010127
+
0x014a50d4
+
0x014a50d8
+
0x014a50dc
+
0x014a694c
+
0x014a69e8
+
0x014a6c80
+
0x01467d70
+
0x01295d58
+
0x01284184
+
0x00c3fb40
+
0x00c23594
+
0x00c68354
+
0x014a66d4
+
0x014a50bc
+
0x014a50c0
+
0x014a50c4
+
+ + 0x47c29583 + 9147b5e922a30873fd1a742b1dea1724 +
0xffffffff
+
0x8b010127
+
+ + 0x487b4e8b + 4b3857a05590b9d9488900e575079e9d +
0x01512b70
+
0x014feb80
+
0x01512504
+
0x00d16c20
+
0x01322d58
+
0x009d6284
+
0x01322adc
+
0x014fec50
+
0x01555f88
+
0x01554028
+
0x01555e28
+
0xffffffff
+
0x8b010131
+
0x01554058
+
0x0155405c
+
0x01554060
+
0x01555ce8
+
0x01555c6c
+
0x01555fa8
+
0x01516cf4
+
0x01330d98
+
0x0131f1b4
+
0x00cd5398
+
0x00cb8dec
+
0x00d03370
+
0x01555658
+
0x01554040
+
0x01554044
+
0x01554048
+
+ + 0x487c9338 + 52155dea390c2080fc16e4bbeb077164 +
0xffffffff
+
0x8b010131
+
+ + 0x487f2f30 + 8f8cf06b1cd5ea102881a7cced767d4f +
0x01513b90
+
0x014ffba0
+
0x01513524
+
0x00d17c44
+
0x01323d78
+
0x009d7284
+
0xffffffff
+ +
0x014ffc70
+
0x01556fa8
+
0x01555048
+
0x01556e48
+
0xffffffff
+ +
0x8b010131
+
0x01555078
+
0x0155507c
+
0x01555080
+
0x01556d08
+
0x01556c8c
+
0x01556fc8
+
0x01517d14
+
0x01331db8
+
0x013201d4
+
0x00cd63bc
+
0x00cb9dec
+
0x00d04394
+
0x01556678
+
0x01555060
+
0x01555064
+
0x01555068
+
+ + 0x48873bc3 + 8614a01593baef6e4a341e2f1a92ba06 +
0x0151ffb8
+
0x0150bfc8
+
0x0151f94c
+
0x00d23c4c
+
0x0132fdb0
+
0x009e3284
+
0x0132fb34
+
0x0150c098
+
0x015635cc
+
0x01561470
+
0x0156346c
+
0x0132faac
+
0x8b010131
+
0x015614a0
+
0x015614a4
+
0x015614a8
+
0x01563154
+
0x015630d8
+
0x015635ec
+
0x0152413c
+
0x0133ddf0
+
0x0132c1dc
+
0x00ce23c4
+
0x00cc5df4
+
0x00d1039c
+
0x01562aa0
+
0x01561488
+
0x0156148c
+
0x01561490
+
+ + 0x4888672c + 32f68422f5b4d938549eed0565bcfb92 + + + 0x489d8c7f + 33db0401081058fb54252210bf371344 +
0x01576468
+
0x01562478
+
0x01575dfc
+
0x00d7a0fc
+
0x01386260
+
0x009ef294
+
0x01385fe4
+
0x01562548
+
0x015b9a7c
+
0x015b7920
+
0x015b991c
+
0x01385f5c
+
0x5f010137
+
0x015b7950
+
0x015b7954
+
0x015b7958
+
0x015b9604
+
0x015b9588
+
0x015b9a9c
+
0x0157a5ec
+
0x013942a0
+
0x0138268c
+
0x00d387fc
+
0x00d66870
+
0x00d6684c
+
0x015b8f50
+
0x015b7938
+
0x015b793c
+
0x015b7940
+
+ + 0x48a9727f + 441c76f45cfffc6abc6548e41c7e2218 +
0x015828a8
+
0x0156e8b8
+
0x0158223c
+
0x00d860fc
+
0x01392268
+
0x009fb294
+
0x01391fc0
+
0x0156e988
+
0x015c5ecc
+
0x015c3d60
+
0x015c5d6c
+
0x01391f64
+
0x5f010138
+
0x015c3d90
+
0x015c3d94
+
0x015c3d98
+
0x015c5a54
+
0x015c59c8
+
0x015c5eec
+
0x01586a2c
+
0x013a02a8
+
0x0138e694
+
0x00d447fc
+
0x00d72870
+
0x00d7284c
+
0x015c5390
+
0x015c3d78
+
0x015c3d7c
+
0x015c3d80
+
+ + 0x48ad547a + 65b4fa339d4081e934c1297d2a22234a + + + 0x48ad802b + 15e95727019e76aa653538618c7e0cfd + + + 0x48c330df + 2c686c26307dcccd7c36cc79737ebe4f +
0x015838a0
+
0x0156f8b0
+
0x01583234
+
0x00D870F4
+
0x01393260
+
0x009fc294
+
0x1583234
+
0x01392fb8
+
0x0156f980
+
0x015c6ed0
+
0x015c4d58
+
0x015c6d70
+
0x01392f5c
+
0x0138147f
+
0x015c4d88
+
0x015c4d8c
+
0x015c4d90
+
0x015c6a58
+
0x015c69cc
+
0x015c6ef0
+
0x01587a24
+
0x013a12a0
+
0x0138f68c
+
0x00d457f4
+
0x00d73868
+
0x00d73844
+
0x015c6388
+
0x015c4d70
+
0x015c4d74
+
0x015c4d78
+
+ + + 0x4953556c + b1a8ca1f91734eb492b7f54f6823cddb + -0x8 + 0x4 +
0x014d1db0
+
0x014bda18
+
0x014D141C
+
0x00cd4c3c
+
0x0165b5a8
+
0x0094b27c
+
0x165b410
+
0x012e101c
+
0x014d14f8
+
0x01517678
+
0x015152f8
+
0x01517468
+
0x012e0fe4
+
0x012cefbf
+
0x01515330
+
0x01515334
+
0x01515338
+
0x015170f8
+
0x0151702c
+
0x015176a8
+
0x014d7f84
+
0x012ef340
+
0x0165a34c
+
0x00c9333c
+
0x00cc13b0
+
0x00cc138c
+
0x01516930
+
0x01515318
+
0x0151531c
+
0x01515320
+ 0x54 + + 0x0 + 0x1C + 0x38 + 0x1C + + + 0xFC + 0x100 + 0x10A + 0x10C + 0x198 + 0x238 + 0x23C + 0x268 + 0x2F8 + 0x314 + 0x3C0 + 0x3C4 + 0x3C8 + 0x484 + 0x4F0 + 0x4F4 + 0x4F8 + 0x504 + 0x51C + 0x544 + 0x610 + 0x700 + + 0x70 + + 0x10 + 0x2C + 0x0082 + 0x0284 + 0x0684 + 0x1D84 + 0x84 + 0x86 + 0x75C + 0x79C + + 0x64 + 0x60 + + 0xC + 0x2 + 0x4 + 0x8 + 0x24 + 0x1C + 0x20 + 0x2C + 0x0 + 0x7a + 0x100 + 0x68 + 0x24 + + 0x18 + 0x1C + 0x28 + 0x24 + 0x24 + 0x24 + 0x5C + 0x94 + 0xB0 + + + + + + + +
+ + 0x4957716f + 9b6da355562a4cdd345ea3046290499b +
0x00cd6c3c
+
0x0094d27c
+
0x012d0fbf
+
0x00c9533c
+
0x00cc33b0
+
0x00cc338c
+
+ + 0x4957a0a2 + b77759db7a6dd787bf98953fc5749d81 + + + 0x495991c3 + a0792b81e5b8ec1dbdd627643e93b40d +
0xcd8c34
+
0x0094f27c
+
0x012d2fbb
+
0x00c97334
+
0x00cc53a8
+
0x00cc5384
+
+ + 0x495cafd2 + d09e88a32fe57de5973f78ef213271b6 +
0xcefc68
+
0x009662a4
+
0x012e9fef
+
0x00cae368
+
0x00cdc3dc
+
0x00cdc3b8
+
+ + 0x495fcfef + 8e8e2a83d421e356a8047dc8830a7426 +
0xcefc68
+
0x009662a4
+
0x012e9fef
+
0x00cae368
+
0x00cdc3dc
+
0x00cdc3b8
+
+ + 0x4963c928 + 32253bee114dd25ebbaa50d90b5c0a2a + + + 0x4967c2e0 + aea5a207b8b1cda942502f97a429f6c3 + + + 0x49c59b94 + 193193d8624f2f3f6d9d556fab09b122 +
0x0151bd00
+
0x01507968
+
0x0151b36c
+
0x00d20ed8
+
0x016a54f8
+
0x009652a4
+
0x16a5360
+
0x0132e574
+
0x0151b448
+
0x015615c8
+
0x0155f248
+
0x015613b8
+
0x0132e53c
+
0x0131b25f
+
0x0155f280
+
0x0155f284
+
0x0155f288
+
0x01561048
+
0x01560f7c
+
0x015615f8
+
0x01521ed4
+
0x0133c898
+
0x016a429c
+
0x00cdf5a0
+
0x00d0d64c
+
0x00d0d628
+
0x01560880
+
0x0155f268
+
0x0155f26c
+
0x0155f270
+
+ + 0x49c82d3f + 6f81231b845e9c9dc29aaf57705ccc7c + + + 0x4a3ccb7f + 6ea1de36af8e1666bd6478736e298c4c +
0x015b7750
+
0x015a33b8
+
0x015b6dbc
+
0x00ddaed8
+
0x0095f410
+
0x0095f2b4
+
0x1740e60
+
0x013e8574
+
0x015b6e98
+
0x015fd04c
+
0x015faccc
+
0x015fce3c
+
0x013e853c
+
0x013d525f
+
0x015fad04
+
0x015fad08
+
0x015fad0c
+
0x015fcacc
+
0x015fca00
+
0x015fd07c
+
0x015bd924
+
0x015b6dc4
+
0x0173fde0
+
0x00d995a0
+
0x00dc764c
+
0x00dc7628
+
0x015fc304
+
0x015facec
+
0x015facf0
+
0x015facf4
+
+ + 0x4a51c26e + 04a8d8ce311d8ac75e4241bef68d3147 +
0x00ddff38
+
0x00964430
+
0x009642b4
+
0x013da2bf
+
0x00d9e600
+
0x00dcc6ac
+
0x00dcc688
+
+ + 0x4a8623d2 + 781a2e51be4056a7320108f8f0df8a13 +
0x00de1f44
+
0x00966430
+
0x009662b4
+
0x013dc2c7
+
0x00da060c
+
0x00dce6b8
+
0x00dce694
+
+ + 0x4a9a6090 + 12cc4a3dbb6e6dfd7bc7aee458b9471a +
0x015be808
+
0x015aa470
+
0x015bde74
+
0x00de1f54
+
0x00966430
+
0x009662b4
+
0x1747f20
+
0x013ef62c
+
0x015bdf50
+
0x01604104
+
0x01601d84
+
0x01603ef4
+
0x013ef5f4
+
0x013dc2eb
+
0x01601dbc
+
0x01601dc0
+
0x01601dc4
+
0x01603b84
+
0x01603ab8
+
0x01604134
+
0x015c49dc
+
0x015bde7c
+
0x01746e98
+
0x00da061c
+
0x00dce6c8
+
0x00dce6a4
+
0x016033bc
+
0x01601da4
+
0x01601da8
+
0x01601dac
+
+ + 0x4a9b1a72 + 59ab29021aca9f3c66b1ab102fb3ceea + + + 0x4b6b7879 + de66405f54d98297303d439b3b7aa30e +
0x015f3260
+
0x015deec8
+
0x015f28cc
+
0x00e16924
+
0x0099ae08
+
0x0099ac88
+
0x177c978
+
0x014240dc
+
0x015f29a8
+
0x01638b5c
+
0x016367dc
+
0x0163894c
+
0x014240a4
+
0x01410cc1
+
0x01636814
+
0x01636818
+
0x0163681c
+
0x016385dc
+
0x01638510
+
0x01638b8c
+
0x015f9434
+
0x014243c4
+
0x0177b8f0
+
0x00dd4fec
+
0x00e03098
+
0x00e03074
+
0x01637e14
+
0x016367fc
+
0x01636800
+
0x01636804
+
0x0177c978
+
+ + + + 0x4b81b00d + 5cdc6f4804809f4d5cacdb66785e8cda + +
0x00df2ebc
+
0x00977438
+
0x009772b8
+
0x014ad278
+
0x00db1584
+
0x00ddf630
+
0x00ddf60c
+
+ + 0x4b90268a + 13640a273d90af39425b798ae9823757 +
0x01512898
+
0x014fe500
+
0x01511f04
+
0x00d35b68
+
0x016ac1a0
+
0x0097a2b8
+
0x169bfb0
+
0x01343714
+
0x01511fe0
+
0x01558194
+
0x01555e14
+
0x01557f84
+
0x013436dc
+
0x0132ff1e
+
0x01555e4c
+
0x01555e50
+
0x01555e54
+
0x01557c14
+
0x01557b48
+
0x015581c4
+
0x01518a6c
+
0x013439fc
+
0x0169af28
+
0x00cf4230
+
0x00d222dc
+
0x00d222b8
+
0x0155744c
+
0x01555e34
+
0x01555e38
+
0x01555e3c
+
+ + 0x4B918BB9 + af29004e1763bb3460faa11907c3ac90 + + .-"""-. + ' \ + |,. ,-. | + |()L( ()| | + |,' `".| | + |.___.',| ` + .j `--"' ` `. + / ' ' \ + / / ` `. + / / ` . + / / l | + . , L I N U X | | + ,"`. .| | + _.' ``. | `..-'l + | `.`, | `. + | `. __.j ) + |__ |--""___| ,-' + `"--...,+"""" `._,.-' + + + + 7a0859795e972574e80fa3cebc9fcf85 + 0x0 + 0x0 +
0x093154e0
+
0x093016b8
+
0x09355940
+
0x09314ffc
+
0x09301560
+
0x08859fc0
+
0x08cfa060
+
0x0930140c
+
0x09356fd0
+
0x09314ffc
+
0x09355964
+
0x09357808
+
0x093013e4
+
0x092f0580
+
0x0935596c
+
0x09355970
+
0x09355974
+
0x0935754c
+
0x093574e0
+
0x09357928
+
0x09318630
+
0x08b36c80
+
0x09355968
+
0x08b36c84
+
0x08cfa978
+
0x08cfa97c
+
0x09356f6c
+
0x09355954
+
0x09355958
+
0x0935595c
+ 0x1C + + 0x0 + 0x4 + 0x8 + 0x4 + + 0x08 + + 0x00 + 0x4C + 0x44 + 0x90 + 0x94 + 0x003c + 0x0040 + 0x009E + 0x00A0 + 0x00F0 + 0x160 + 0x164 + 0x190 + 0x02F8 + 0x0200 + 0x264 + 0x268 + 0x26C + 0x02F8 + 0x0334 + 0x0338 + 0x033C + 0x0340 + 0x034C + 0x0364 + 0x0400 + 0x0490 + + 0x40 + + 0x08 + 0x2C + 0x0052 + 0x0254 + 0x0654 + 0x1D54 + + 0x54 + 0x56 + 0x5A4 + 0x5C8 + + 0x58 + 0x54 + + 0x4 + + 0xC + 0x24 + 0x18 + 0x2 + 0x4 + 0x8 + 0xC + 0x4 + 0x8 + 0x14 + 0x00 + 0x4a + 0x94 + 0x50 + 0x0C + + 0x20 + + 0xC + 0x4 + 0x10 + 0xC + 0xC + 0xC + 0x14 + 0x1C + 0x20 + + + + + + +
+ + 51c73ff46b2688aafaee0204efe91a94 +
0x09315f00
+
0x093020d8
+
0x09356360
+
0x09315a1c
+
0x09301f80
+
0x0885a9e4
+
0x08cfaa80
+
0x09301e2c
+
0x093579f0
+
0x09315a1c
+
0x09356384
+
0x09358228
+
0x09301e04
+
0x092f0fa0
+
0x0935638c
+
0x09356390
+
0x09356394
+
0x09357f6c
+
0x09357f00
+
0x09358348
+
0x09319050
+
0x08b376a0
+
0x09356388
+
0x08b376a4
+
0x08cfb398
+
0x08cfb39c
+
0x0935798c
+
0x09356374
+
0x09356378
+
0x0935637c
+
+ + c1eb408868c80fd1c726d2a917cd1b9a +
0x0885ad54
+
+ + 59d497bfc3a523f0f40f34283ad59796 +
0x0885bbf0
+
+ + f756194db073f05b98fc6ce872c8757d +
0x09333e00
+
0x0931ffd8
+
0x09374260
+
0x0933391c
+
0x0931fe80
+
0x088788e4
+
0x08d18980
+
0x0931fd2c
+
0x093758f0
+
0x0933391c
+
0x09374284
+
0x09376128
+
0x0931fd04
+
0x0930eea0
+
0x0937428c
+
0x09374290
+
0x09374294
+
0x09375e6c
+
0x09375e00
+
0x09376248
+
0x09336f50
+
0x08b555a0
+
0x09374288
+
0x08b555a4
+
0x08d19298
+
0x08d1929c
+
0x0937588c
+
0x09374274
+
0x09374278
+
0x0937427c
+
+ + b004b3876193633875956af752663f26 + + + c8616fc74d79b3c8c40bbc1182fbd61c +
0x088786a0
+
+ + 992afd73855e787860277f53d18afcbb +
0x08877630
+
+ + cba6354000ec54865a161627605c3837 +
0x092bf340
+
0x092ab518
+
0x092bee5c
+
0x0929a3c8
+
0x092ab3c0
+
0x088073d4
+
0x08ca3eb8
+
0x092ab26c
+
0x092beecc
+
0x09301770
+
0x092ff7a0
+
0x09301668
+
0x092ab244
+
0x0929a3e0
+
0x092ff7cc
+
0x092ff7d0
+
0x092ff7d4
+
0x093013ac
+
0x09301340
+
0x09301788
+
0x092c2490
+
0x08ae40a0
+
0x09510050
+
0x08ca47d4
+
0x08ca47d8
+
0x08ca47dc
+
0x09300dcc
+
0x092ff7b4
+
0x092ff7b8
+
0x092ff7bc
+
+ + fb8ecac8a12af5d0d7b1707078985d0d + + + 4367c59934cbcf14f43fd3af6444c455 +
0x08f55740
+
0x08f41918
+
0x08f5525c
+
0x08f307c8
+
0x08f417c0
+
0x0877b33c
+
0x0893a2ac
+
0x08f4166c
+
0x08f552cc
+
0x08f97b8c
+
0x08f95bbc
+
0x08f97a84
+
0x08f41644
+
0x08f307e0
+
0x08f95be8
+
0x08f95bec
+
0x08f95bf0
+
0x08f977c8
+
0x08f9775c
+
0x08f97ba4
+
0x08f58890
+
0x0877f8e0
+
0x091a647c
+
0x0893abd4
+
0x0893abd8
+
0x0893abdc
+
0x08f971e8
+
0x08f95bd0
+
0x08f95bd4
+
0x08f95bd8
+
+ + 2f3cb9d720e9fe8844c02c72a2b20bbd +
0x08780344
+
0x0893f2d0
+
+ + dab3ce6bc074529706a1e5fe1273108c +
0x08f5a760
+
0x08f46938
+
0x08f5a27c
+
0x08f357e8
+
0x08f467e0
+
0x08780354
+
0x0893f2d0
+
0x08f4668c
+
0x08f5a2ec
+
0x08f9cbac
+
0x08f9abdc
+
0x08f9caa4
+
0x08f46664
+
0x08f35800
+
0x08f9ac08
+
0x08f9ac0c
+
0x08f9ac10
+
0x08f9c7e8
+
0x08f9c77c
+
0x08f9cbc4
+
0x08f5d8b0
+
0x08784900
+
0x091ab49c
+
0x0893fbf4
+
0x0893fbf8
+
0x0893fbfc
+
0x08f9c208
+
0x08f9abf0
+
0x08f9abf4
+
0x08f9abf8
+
+ + 4f55a1dcc326786271f221de23c425b5 + + + 022b933926e08da49c6df8649295f2b7 + + + 8f55a6250f2550e28535b79db43d5f1a +
0x08f628c0
+
0x08f4ea98
+
0x08f623dc
+
0x08f3d948
+
0x08f4e940
+
0x0878c340
+
0x08947438
+
0x08f4e7ec
+
0x08f6244c
+
0x08fa4d0c
+
0x08fa2d3c
+
0x08fa4c04
+
0x08f4e7c4
+
0x660008f3
+
0x08fa2d68
+
0x08fa2d6c
+
0x08fa2d70
+
0x08fa4948
+
0x08fa48dc
+
0x08fa4d24
+
0x08f65a10
+
0x0878caa0
+
0x091b35fc
+
0x08947d54
+
0x08947d58
+
0x08947d5c
+
0x08fa4368
+
0x08fa2d50
+
0x08fa2d54
+
0x08fa2d58
+
+ + 777e7d674d8908042307994cb75250ff +
0x09009860
+
0x08ff5a38
+
0x0900937C
+
0x08fe48e8
+
0x08ff58e0
+
0x08833324
+
0x089ee3d8
+
0x08FF578C
+
0x090093ec
+
0x0904bcac
+
0x09049cdc
+
0x0904bba4
+
0x08ff5764
+
0x08fe4900
+
0x09049d08
+
0x09049d0c
+
0x09049d10
+
0x0904b8e8
+
0x0904b87c
+
0x0904bcc4
+
0x0900c9b0
+
0x08833a40
+
0x0925a59c
+
0x089eecf4
+
0x089eecf8
+
0x089eecfc
+
0x0904b308
+
0x09049cf0
+
0x09049cf4
+
0x09049cf8
+
+ + 04c3ad13c657f59ba6fc135e156d721d +
0x09008880
+
0x08ff4a58
+
0x0900839C
+
0x08fe3908
+
0x08ff4900
+
0x08832328
+
0x089ed3f8
+
0x08ff47ac
+
0x0900840c
+
0x0904accc
+
0x09048cfc
+
0x0904abc4
+
0x08ff4784
+
0xc60008fe
+
0x09048d28
+
0x09048d2c
+
0x09048d30
+
0x0904a908
+
0x0904a89c
+
0x0904ace4
+
0x0900b9d0
+
0x08832a60
+
0x091995fc
+
0x089edd14
+
0x089edd18
+
0x089edd1c
+
0x0904a328
+
0x09048d10
+
0x09048d14
+
0x09048d18
+
+ + + 851c1190b6a7b42f2463967623d18575 + 0x4BB45F99 + + + 0x18 + 0x0 + 0xC + 0x1C + + +
0xe32798
+
0xe60838
+
0xe60814
+
0xae82cc
+ +
0x17f5ab8
+ Found addresses: (next to each other!) + 0x17f5ab8 + 0x17f5ac0 + 0x17f5ac8 + 0x17f5ad0 + +
0x146e45f
+ Found addresses: + 0x146e45f + 0x185b677 + + Bogus:
0xae82cc
+ +
0x017f6f38
+ + Bogus:
0xae82cc
+ + + +
0x016ad738
+
0x016ad73C
+
0x016ad740
+ +
0x016ad744
+
0x016ad748
+
0x016ad74C
+ +
0x016ad750
+
0x016ad754
+
0x016ad758
+ +
0x016AD718
+ 0x10 + 0x0092 + 0x029C + + + 0x0 + 0x1C + 0x38 + + + 0x0 + 0x90 + 0xF8 + 0xFC + 0x110 + 0x114 + 0x6D0 + 0x770 + + + + +
+
+ diff --git a/output/Memory.xml b/output/Memory.xml index b6d5ce1f5..bba958a69 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -3,789 +3,39 @@ - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 - 48 - 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - 57 - 58 - 59 - 60 - 61 - 62 - 63 - 64 - 65 - 66 - 67 - 68 - 69 - 70 - 71 - 72 - 73 - 74 - 75 - 76 - 77 - 78 - 79 - 80 - 81 - 82 - 83 - 84 - 85 - 86 - 87 - 88 - 89 - 90 - 91 - 92 - 93 - 94 - 95 - 96 - 97 - 98 - 99 - 100 - 101 - 102 - 103 - 104 - 105 - 106 - 107 - 108 - 109 - 110 - 111 - 112 - 113 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 - 48 - 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - 57 - 58 - 59 - 60 - 61 - 62 - 63 - 64 - 65 - 66 - 67 - 68 - 69 - 70 - 71 - 72 - 73 - 74 - 75 - 76 - 77 - 78 - 79 - 80 - 81 - 82 - 83 - 84 - 85 - 86 - 87 - 88 - 89 - 90 - 91 - 92 - 93 - 94 - 95 - 96 - 97 - 98 - 99 - 100 - 101 - 102 - 103 - 104 - 105 - 106 - 107 - 108 - 109 - 110 - 111 - 112 - 113 - 114 - 115 - 116 - 117 - 118 - 119 - 120 - 121 - 122 - 123 - 124 - 125 - 126 - 127 - 128 - 129 - 130 - 131 - 132 - 133 - 134 - 135 - 136 - 137 - 138 - 139 - 140 - 141 - 142 - 143 - 144 - 145 - 146 - 147 - 148 - 149 - 150 - 151 - 152 - 153 - 154 - 155 - 156 - 157 - 158 - 159 - 160 - 161 - 162 - 163 - 164 - 165 - 166 - 167 - 168 - 169 - 170 - 171 - 172 - 173 - 174 - 175 - 176 - 177 - 178 - 179 - 180 - 181 - 182 - 183 - 184 - 185 - 186 - 187 - 188 - 189 - 190 - 191 - 192 - 193 - 194 - 195 - 196 - 197 - 198 - 199 - 200 - 201 - 202 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 - 38 - 39 - 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 - 48 - 49 - 50 - 51 - 52 - 53 - 54 - 55 - 56 - 57 - 58 - 59 - 60 - 61 - 62 - 63 - 64 - 65 - 66 - 67 - 68 - 69 - 70 - 71 - 72 - 73 - 74 - 75 - 76 - 77 - 78 - 79 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 0 - 66 - 11 - 10 - 13 - 12 - 14 - 15 - 16 - 20 - 38 - 19 - 41 - 39 - 43 - 42 - 40 - 44 - 45 - 48 - 27 - 49 - 21 - 47 - 26 - 46 - 50 - 23 - 18 - 30 - 32 - 71 - 33 - 34 - 70 - 31 - 28 - 69 - 25 - 22 - 29 - 72 - 35 - 36 - 37 - 67 - 73 - 64 - 65 - 17 - 8 - 2 - 6 - 4 - 1 - 5 - 7 - 9 - 3 - 24 - 51 - 52 - 53 - 54 - 55 - 57 - -1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + --> - - .,:rsr, - :2;,;r2A@@5 - @2::s5A#@@@ @r. . - sd;:riXA#@@ :@@@Gir;;AS9 - Bs::sS3A#@2 @@#AhXirsS#; - iHrLr5d#@@@ .@#95sr;;rie - i*' `*@3 @@A2sr;:;r#5 - :..:rll: @@A5sr::r3@ - @Hr;iZ#@@@@ `:rr;;;;: - S@r.;i2#@@@ @s. .. - @2::ri2A@@# B@G2ir:...5i - :@r,r3X##@@ @G5sr:..,:A - .@Ar;;rSB@@# H#2sr;,..,is - .' `* ,@ASs;:..,:B - ;rr;:,..,:. - `''' - W I N D O W S - and - W I N E - + - 0x4 + + where a vector actually starts: 0x0 + where the vector triplet is: 0x4 +
0x014639f4
0x01463288
0x01463388
@@ -815,21 +65,29 @@
0x014a4ec4
0x014a4ec8
0x014a4ecc
- + + job object offsets + ================== 0x08 - + + map block offsets + ================= 0x08 0x2C 0x0062 0x0264 0x0664 0x1D64 - + + name struct + =========== 0x0 0x1C 0x38 0x1C - + + creature offsets + ================ 0x00 0x6c 0x88 @@ -857,29 +115,45 @@ 0x474 0x520 0x5D0 - + + tree and shrub offsets + ====================== 0x70 - + + the world and its offsets + ========================= 0x84 0x86 0x684 0x6B4 - + + values for the region structure + =============================== 0x5C 0x58 - + + geoblock offset(s?) + =================== 0x4 - + + matgloss vectors + ================ 0x10 - + + only stone and metal have color loaded... + ========================================= 0x84 0x60 + + map block offsets + ================= 0x08 0x2C 0x0062 0x0264 0x0664 0x1D64 + 0x3C 0x2 0x4 @@ -892,6 +166,8 @@ 0xD8 0x68 0x24 + + 0x1C 0x28 0x24 @@ -901,655 +177,184 @@ 0x94 0xB0 - - 0x47c12f36 - 8bd90fb6db8388f129fde224a35459de -
0x01463bec
-
0x01463480
-
0x01463580
-
0x00c7bc04
-
0x01287d14
-
0x0094128c
-
0x01287a9c
-
0x01463550
-
0x014a6c60
-
0x014a50a4
-
0x014a6b00
-
0xffffffff
-
0x8b010127
-
0x014a50d4
-
0x014a50d8
-
0x014a50dc
-
0x014a694c
-
0x014a69e8
-
0x014a6c80
-
0x01467d70
-
0x01295d58
-
0x01284184
-
0x00c3fb40
-
0x00c23594
-
0x00c68354
-
0x014a66d4
-
0x014a50bc
-
0x014a50c0
-
0x014a50c4
-
- - 0x47c29583 - 9147b5e922a30873fd1a742b1dea1724 -
0xffffffff
-
0x8b010127
-
- - 0x487b4e8b - 4b3857a05590b9d9488900e575079e9d -
0x01512b70
-
0x014feb80
-
0x01512504
-
0x00d16c20
-
0x01322d58
-
0x009d6284
-
0x01322adc
-
0x014fec50
-
0x01555f88
-
0x01554028
-
0x01555e28
-
0xffffffff
-
0x8b010131
-
0x01554058
-
0x0155405c
-
0x01554060
-
0x01555ce8
-
0x01555c6c
-
0x01555fa8
-
0x01516cf4
-
0x01330d98
-
0x0131f1b4
-
0x00cd5398
-
0x00cb8dec
-
0x00d03370
-
0x01555658
-
0x01554040
-
0x01554044
-
0x01554048
-
- - 0x487c9338 - 52155dea390c2080fc16e4bbeb077164 -
0xffffffff
-
0x8b010131
-
- - 0x487f2f30 - 8f8cf06b1cd5ea102881a7cced767d4f -
0x01513b90
-
0x014ffba0
-
0x01513524
-
0x00d17c44
-
0x01323d78
-
0x009d7284
-
0xffffffff
- -
0x014ffc70
-
0x01556fa8
-
0x01555048
-
0x01556e48
-
0xffffffff
- -
0x8b010131
-
0x01555078
-
0x0155507c
-
0x01555080
-
0x01556d08
-
0x01556c8c
-
0x01556fc8
-
0x01517d14
-
0x01331db8
-
0x013201d4
-
0x00cd63bc
-
0x00cb9dec
-
0x00d04394
-
0x01556678
-
0x01555060
-
0x01555064
-
0x01555068
-
- - 0x48873bc3 - 8614a01593baef6e4a341e2f1a92ba06 -
0x0151ffb8
-
0x0150bfc8
-
0x0151f94c
-
0x00d23c4c
-
0x0132fdb0
-
0x009e3284
-
0x0132fb34
-
0x0150c098
-
0x015635cc
-
0x01561470
-
0x0156346c
-
0x0132faac
-
0x8b010131
-
0x015614a0
-
0x015614a4
-
0x015614a8
-
0x01563154
-
0x015630d8
-
0x015635ec
-
0x0152413c
-
0x0133ddf0
-
0x0132c1dc
-
0x00ce23c4
-
0x00cc5df4
-
0x00d1039c
-
0x01562aa0
-
0x01561488
-
0x0156148c
-
0x01561490
-
- - 0x4888672c - 32f68422f5b4d938549eed0565bcfb92 - - - 0x489d8c7f - 33db0401081058fb54252210bf371344 -
0x01576468
-
0x01562478
-
0x01575dfc
-
0x00d7a0fc
-
0x01386260
-
0x009ef294
-
0x01385fe4
-
0x01562548
-
0x015b9a7c
-
0x015b7920
-
0x015b991c
-
0x01385f5c
-
0x5f010137
-
0x015b7950
-
0x015b7954
-
0x015b7958
-
0x015b9604
-
0x015b9588
-
0x015b9a9c
-
0x0157a5ec
-
0x013942a0
-
0x0138268c
-
0x00d387fc
-
0x00d66870
-
0x00d6684c
-
0x015b8f50
-
0x015b7938
-
0x015b793c
-
0x015b7940
-
- - 0x48a9727f - 441c76f45cfffc6abc6548e41c7e2218 -
0x015828a8
-
0x0156e8b8
-
0x0158223c
-
0x00d860fc
-
0x01392268
-
0x009fb294
-
0x01391fc0
-
0x0156e988
-
0x015c5ecc
-
0x015c3d60
-
0x015c5d6c
-
0x01391f64
-
0x5f010138
-
0x015c3d90
-
0x015c3d94
-
0x015c3d98
-
0x015c5a54
-
0x015c59c8
-
0x015c5eec
-
0x01586a2c
-
0x013a02a8
-
0x0138e694
-
0x00d447fc
-
0x00d72870
-
0x00d7284c
-
0x015c5390
-
0x015c3d78
-
0x015c3d7c
-
0x015c3d80
-
- - 0x48ad547a - 65b4fa339d4081e934c1297d2a22234a - - - 0x48ad802b - 15e95727019e76aa653538618c7e0cfd - - - 0x48c330df - 2c686c26307dcccd7c36cc79737ebe4f -
0x015838a0
-
0x0156f8b0
-
0x01583234
-
0x00D870F4
-
0x01393260
-
0x009fc294
-
0x1583234
-
0x01392fb8
-
0x0156f980
-
0x015c6ed0
-
0x015c4d58
-
0x015c6d70
-
0x01392f5c
-
0x0138147f
-
0x015c4d88
-
0x015c4d8c
-
0x015c4d90
-
0x015c6a58
-
0x015c69cc
-
0x015c6ef0
-
0x01587a24
-
0x013a12a0
-
0x0138f68c
-
0x00d457f4
-
0x00d73868
-
0x00d73844
-
0x015c6388
-
0x015c4d70
-
0x015c4d74
-
0x015c4d78
-
- - - 0x4953556c - b1a8ca1f91734eb492b7f54f6823cddb - -0x8 - 0x4 -
0x014d1db0
-
0x014bda18
-
0x014D141C
-
0x00cd4c3c
-
0x0165b5a8
-
0x0094b27c
-
0x165b410
-
0x012e101c
-
0x014d14f8
-
0x01517678
-
0x015152f8
-
0x01517468
-
0x012e0fe4
-
0x012cefbf
-
0x01515330
-
0x01515334
-
0x01515338
-
0x015170f8
-
0x0151702c
-
0x015176a8
-
0x014d7f84
-
0x012ef340
-
0x0165a34c
-
0x00c9333c
-
0x00cc13b0
-
0x00cc138c
-
0x01516930
-
0x01515318
-
0x0151531c
-
0x01515320
- 0x54 - - 0x0 - 0x1C - 0x38 - 0x1C - - - 0xFC - 0x100 - 0x10A - 0x10C - 0x198 - 0x238 - 0x23C - 0x268 - 0x2F8 - 0x314 - 0x3C0 - 0x3C4 - 0x3C8 - 0x484 - 0x4F0 - 0x4F4 - 0x4F8 - 0x504 - 0x51C - 0x544 - 0x610 - 0x700 - - 0x70 - - 0x10 - 0x2C - 0x0082 - 0x0284 - 0x0684 - 0x1D84 - 0x84 - 0x86 - 0x75C - 0x79C - - 0x64 - 0x60 - - 0xC - 0x2 - 0x4 - 0x8 - 0x24 - 0x1C - 0x20 - 0x2C - 0x0 - 0x7a - 0x100 - 0x68 - 0x24 - - 0x18 - 0x1C - 0x28 - 0x24 - 0x24 - 0x24 - 0x5C - 0x94 - 0xB0 - - - - - - - -
- - 0x4957716f - 9b6da355562a4cdd345ea3046290499b -
0x00cd6c3c
-
0x0094d27c
-
0x012d0fbf
-
0x00c9533c
-
0x00cc33b0
-
0x00cc338c
-
- - 0x4957a0a2 - b77759db7a6dd787bf98953fc5749d81 - - - 0x495991c3 - a0792b81e5b8ec1dbdd627643e93b40d -
0xcd8c34
-
0x0094f27c
-
0x012d2fbb
-
0x00c97334
-
0x00cc53a8
-
0x00cc5384
-
- - 0x495cafd2 - d09e88a32fe57de5973f78ef213271b6 -
0xcefc68
-
0x009662a4
-
0x012e9fef
-
0x00cae368
-
0x00cdc3dc
-
0x00cdc3b8
-
- - 0x495fcfef - 8e8e2a83d421e356a8047dc8830a7426 -
0xcefc68
-
0x009662a4
-
0x012e9fef
-
0x00cae368
-
0x00cdc3dc
-
0x00cdc3b8
-
- - 0x4963c928 - 32253bee114dd25ebbaa50d90b5c0a2a - - - 0x4967c2e0 - aea5a207b8b1cda942502f97a429f6c3 - - - 0x49c59b94 - 193193d8624f2f3f6d9d556fab09b122 -
0x0151bd00
-
0x01507968
-
0x0151b36c
-
0x00d20ed8
-
0x016a54f8
-
0x009652a4
-
0x16a5360
-
0x0132e574
-
0x0151b448
-
0x015615c8
-
0x0155f248
-
0x015613b8
-
0x0132e53c
-
0x0131b25f
-
0x0155f280
-
0x0155f284
-
0x0155f288
-
0x01561048
-
0x01560f7c
-
0x015615f8
-
0x01521ed4
-
0x0133c898
-
0x016a429c
-
0x00cdf5a0
-
0x00d0d64c
-
0x00d0d628
-
0x01560880
-
0x0155f268
-
0x0155f26c
-
0x0155f270
-
- - 0x49c82d3f - 6f81231b845e9c9dc29aaf57705ccc7c - - - 0x4a3ccb7f - 6ea1de36af8e1666bd6478736e298c4c -
0x015b7750
-
0x015a33b8
-
0x015b6dbc
-
0x00ddaed8
-
0x0095f410
-
0x0095f2b4
-
0x1740e60
-
0x013e8574
-
0x015b6e98
-
0x015fd04c
-
0x015faccc
-
0x015fce3c
-
0x013e853c
-
0x013d525f
-
0x015fad04
-
0x015fad08
-
0x015fad0c
-
0x015fcacc
-
0x015fca00
-
0x015fd07c
-
0x015bd924
-
0x015b6dc4
-
0x0173fde0
-
0x00d995a0
-
0x00dc764c
-
0x00dc7628
-
0x015fc304
-
0x015facec
-
0x015facf0
-
0x015facf4
-
- - 0x4a51c26e - 04a8d8ce311d8ac75e4241bef68d3147 -
0x00ddff38
-
0x00964430
-
0x009642b4
-
0x013da2bf
-
0x00d9e600
-
0x00dcc6ac
-
0x00dcc688
-
- - 0x4a8623d2 - 781a2e51be4056a7320108f8f0df8a13 -
0x00de1f44
-
0x00966430
-
0x009662b4
-
0x013dc2c7
-
0x00da060c
-
0x00dce6b8
-
0x00dce694
-
- - 0x4a9a6090 - 12cc4a3dbb6e6dfd7bc7aee458b9471a -
0x015be808
-
0x015aa470
-
0x015bde74
-
0x00de1f54
-
0x00966430
-
0x009662b4
-
0x1747f20
-
0x013ef62c
-
0x015bdf50
-
0x01604104
-
0x01601d84
-
0x01603ef4
-
0x013ef5f4
-
0x013dc2eb
-
0x01601dbc
-
0x01601dc0
-
0x01601dc4
-
0x01603b84
-
0x01603ab8
-
0x01604134
-
0x015c49dc
-
0x015bde7c
-
0x01746e98
-
0x00da061c
-
0x00dce6c8
-
0x00dce6a4
-
0x016033bc
-
0x01601da4
-
0x01601da8
-
0x01601dac
-
- - 0x4a9b1a72 - 59ab29021aca9f3c66b1ab102fb3ceea - - - 0x4b6b7879 - de66405f54d98297303d439b3b7aa30e -
0x015f3260
-
0x015deec8
-
0x015f28cc
-
0x00e16924
-
0x0099ae08
-
0x0099ac88
-
0x177c978
-
0x014240dc
-
0x015f29a8
-
0x01638b5c
-
0x016367dc
-
0x0163894c
-
0x014240a4
-
0x01410cc1
-
0x01636814
-
0x01636818
-
0x0163681c
-
0x016385dc
-
0x01638510
-
0x01638b8c
-
0x015f9434
-
0x014243c4
-
0x0177b8f0
-
0x00dd4fec
-
0x00e03098
-
0x00e03074
-
0x01637e14
-
0x016367fc
-
0x01636800
-
0x01636804
-
0x0177c978
-
- - - - 0x4b81b00d - 5cdc6f4804809f4d5cacdb66785e8cda - -
0x00df2ebc
-
0x00977438
-
0x009772b8
-
0x014ad278
-
0x00db1584
-
0x00ddf630
-
0x00ddf60c
-
- - 0x4b90268a - 13640a273d90af39425b798ae9823757 -
0x01512898
-
0x014fe500
-
0x01511f04
-
0x00d35b68
-
0x016ac1a0
-
0x0097a2b8
-
0x169bfb0
-
0x01343714
-
0x01511fe0
-
0x01558194
-
0x01555e14
-
0x01557f84
-
0x013436dc
-
0x0132ff1e
-
0x01555e4c
-
0x01555e50
-
0x01555e54
-
0x01557c14
-
0x01557b48
-
0x015581c4
-
0x01518a6c
-
0x013439fc
-
0x0169af28
-
0x00cf4230
-
0x00d222dc
-
0x00d222b8
-
0x0155744c
-
0x01555e34
-
0x01555e38
-
0x01555e3c
-
- - 0x4B918BB9 - af29004e1763bb3460faa11907c3ac90 + --> + + + .,:rsr, + :2;,;r2A@@5 + @2::s5A#@@@ @r. . + sd;:riXA#@@ :@@@Gir;;AS9 + Bs::sS3A#@2 @@#AhXirsS#; + iHrLr5d#@@@ .@#95sr;;rie + i*' `*@3 @@A2sr;:;r#5 + :..:rll: @@A5sr::r3@ + @Hr;iZ#@@@@ `:rr;;;;: + S@r.;i2#@@@ @s. .. + @2::ri2A@@# B@G2ir:...5i + :@r,r3X##@@ @G5sr:..,:A + .@Ar;;rSB@@# H#2sr;,..,is + .' `* ,@ASs;:..,:B + ;rr;:,..,:. + `''' + W I N D O W S + and + W I N E + + + + + 851c1190b6a7b42f2463967623d18575 + 0x4BB45F99 + + Basic things + ============ + 0x18 + 0xC + 0x1C + + Position and window dimensions + ============================== +
0xe32798
+
0xe60838
+
0xe60814
+
0xae82cc
+ +
0x17f5ab8
+ Found addresses: (next to each other!) + 0x17f5ab8 + 0x17f5ac0 + 0x17f5ac8 + 0x17f5ad0 + +
0x146e45f
+ Found addresses: + 0x146e45f + 0x185b677 + + Bogus:
0xae82cc
+ +
0x017f6f38
+ + Bogus:
0xae82cc
+ + Map stuff + ========= +
0x016AD718
+ 0x10 + 0x0092 + 0x029C + + + * map size in blocks * +
0x016ad738
+
0x016ad73C
+
0x016ad740
+ + * map size in tiles * +
0x016ad744
+
0x016ad748
+
0x016ad74C
+ + * Suspected region coords * +
0x016ad750
+
0x016ad754
+
0x016ad758
+ + name struct + =========== + 0x0 + 0x1C + 0x38 + + Creatures + ========= + 0x0 + 0x90 + 0xF8 + 0xFC + 0x110 + 0x114 + 0x6D0 + 0x770 + + + +
+ .-"""-. ' \ - |,. ,-. | - |()L( ()| | - |,' `".| | - |.___.',| ` + |,. ,-. | _________________________ + |()L( ()| | \ \ + |,' `".| | /_ Nothing here yet :( \ + |.___.',| ` \________________________\ .j `--"' ` `. / ' ' \ / / ` `. @@ -1562,568 +367,8 @@ | `. __.j ) |__ |--""___| ,-' `"--...,+"""" `._,.-' - - - 7a0859795e972574e80fa3cebc9fcf85 - 0x0 - 0x0 -
0x093154e0
-
0x093016b8
-
0x09355940
-
0x09314ffc
-
0x09301560
-
0x08859fc0
-
0x08cfa060
-
0x0930140c
-
0x09356fd0
-
0x09314ffc
-
0x09355964
-
0x09357808
-
0x093013e4
-
0x092f0580
-
0x0935596c
-
0x09355970
-
0x09355974
-
0x0935754c
-
0x093574e0
-
0x09357928
-
0x09318630
-
0x08b36c80
-
0x09355968
-
0x08b36c84
-
0x08cfa978
-
0x08cfa97c
-
0x09356f6c
-
0x09355954
-
0x09355958
-
0x0935595c
- 0x1C - - 0x0 - 0x4 - 0x8 - 0x4 - - 0x08 - - 0x00 - 0x4C - 0x44 - 0x90 - 0x94 - 0x003c - 0x0040 - 0x009E - 0x00A0 - 0x00F0 - 0x160 - 0x164 - 0x190 - 0x02F8 - 0x0200 - 0x264 - 0x268 - 0x26C - 0x02F8 - 0x0334 - 0x0338 - 0x033C - 0x0340 - 0x034C - 0x0364 - 0x0400 - 0x0490 - - 0x40 - - 0x08 - 0x2C - 0x0052 - 0x0254 - 0x0654 - 0x1D54 - - 0x54 - 0x56 - 0x5A4 - 0x5C8 - - 0x58 - 0x54 - - 0x4 - - 0xC - 0x24 - 0x18 - 0x2 - 0x4 - 0x8 - 0xC - 0x4 - 0x8 - 0x14 - 0x00 - 0x4a - 0x94 - 0x50 - 0x0C - - 0x20 - - 0xC - 0x4 - 0x10 - 0xC - 0xC - 0xC - 0x14 - 0x1C - 0x20 - - - - - - -
- - 51c73ff46b2688aafaee0204efe91a94 -
0x09315f00
-
0x093020d8
-
0x09356360
-
0x09315a1c
-
0x09301f80
-
0x0885a9e4
-
0x08cfaa80
-
0x09301e2c
-
0x093579f0
-
0x09315a1c
-
0x09356384
-
0x09358228
-
0x09301e04
-
0x092f0fa0
-
0x0935638c
-
0x09356390
-
0x09356394
-
0x09357f6c
-
0x09357f00
-
0x09358348
-
0x09319050
-
0x08b376a0
-
0x09356388
-
0x08b376a4
-
0x08cfb398
-
0x08cfb39c
-
0x0935798c
-
0x09356374
-
0x09356378
-
0x0935637c
-
- - c1eb408868c80fd1c726d2a917cd1b9a -
0x0885ad54
-
- - 59d497bfc3a523f0f40f34283ad59796 -
0x0885bbf0
-
- - f756194db073f05b98fc6ce872c8757d -
0x09333e00
-
0x0931ffd8
-
0x09374260
-
0x0933391c
-
0x0931fe80
-
0x088788e4
-
0x08d18980
-
0x0931fd2c
-
0x093758f0
-
0x0933391c
-
0x09374284
-
0x09376128
-
0x0931fd04
-
0x0930eea0
-
0x0937428c
-
0x09374290
-
0x09374294
-
0x09375e6c
-
0x09375e00
-
0x09376248
-
0x09336f50
-
0x08b555a0
-
0x09374288
-
0x08b555a4
-
0x08d19298
-
0x08d1929c
-
0x0937588c
-
0x09374274
-
0x09374278
-
0x0937427c
-
- - b004b3876193633875956af752663f26 - - - c8616fc74d79b3c8c40bbc1182fbd61c -
0x088786a0
-
- - 992afd73855e787860277f53d18afcbb -
0x08877630
-
- - cba6354000ec54865a161627605c3837 -
0x092bf340
-
0x092ab518
-
0x092bee5c
-
0x0929a3c8
-
0x092ab3c0
-
0x088073d4
-
0x08ca3eb8
-
0x092ab26c
-
0x092beecc
-
0x09301770
-
0x092ff7a0
-
0x09301668
-
0x092ab244
-
0x0929a3e0
-
0x092ff7cc
-
0x092ff7d0
-
0x092ff7d4
-
0x093013ac
-
0x09301340
-
0x09301788
-
0x092c2490
-
0x08ae40a0
-
0x09510050
-
0x08ca47d4
-
0x08ca47d8
-
0x08ca47dc
-
0x09300dcc
-
0x092ff7b4
-
0x092ff7b8
-
0x092ff7bc
-
- - fb8ecac8a12af5d0d7b1707078985d0d - - - 4367c59934cbcf14f43fd3af6444c455 -
0x08f55740
-
0x08f41918
-
0x08f5525c
-
0x08f307c8
-
0x08f417c0
-
0x0877b33c
-
0x0893a2ac
-
0x08f4166c
-
0x08f552cc
-
0x08f97b8c
-
0x08f95bbc
-
0x08f97a84
-
0x08f41644
-
0x08f307e0
-
0x08f95be8
-
0x08f95bec
-
0x08f95bf0
-
0x08f977c8
-
0x08f9775c
-
0x08f97ba4
-
0x08f58890
-
0x0877f8e0
-
0x091a647c
-
0x0893abd4
-
0x0893abd8
-
0x0893abdc
-
0x08f971e8
-
0x08f95bd0
-
0x08f95bd4
-
0x08f95bd8
-
- - 2f3cb9d720e9fe8844c02c72a2b20bbd -
0x08780344
-
0x0893f2d0
-
- - dab3ce6bc074529706a1e5fe1273108c -
0x08f5a760
-
0x08f46938
-
0x08f5a27c
-
0x08f357e8
-
0x08f467e0
-
0x08780354
-
0x0893f2d0
-
0x08f4668c
-
0x08f5a2ec
-
0x08f9cbac
-
0x08f9abdc
-
0x08f9caa4
-
0x08f46664
-
0x08f35800
-
0x08f9ac08
-
0x08f9ac0c
-
0x08f9ac10
-
0x08f9c7e8
-
0x08f9c77c
-
0x08f9cbc4
-
0x08f5d8b0
-
0x08784900
-
0x091ab49c
-
0x0893fbf4
-
0x0893fbf8
-
0x0893fbfc
-
0x08f9c208
-
0x08f9abf0
-
0x08f9abf4
-
0x08f9abf8
-
- - 4f55a1dcc326786271f221de23c425b5 - - - 022b933926e08da49c6df8649295f2b7 - - - 8f55a6250f2550e28535b79db43d5f1a -
0x08f628c0
-
0x08f4ea98
-
0x08f623dc
-
0x08f3d948
-
0x08f4e940
-
0x0878c340
-
0x08947438
-
0x08f4e7ec
-
0x08f6244c
-
0x08fa4d0c
-
0x08fa2d3c
-
0x08fa4c04
-
0x08f4e7c4
-
0x660008f3
-
0x08fa2d68
-
0x08fa2d6c
-
0x08fa2d70
-
0x08fa4948
-
0x08fa48dc
-
0x08fa4d24
-
0x08f65a10
-
0x0878caa0
-
0x091b35fc
-
0x08947d54
-
0x08947d58
-
0x08947d5c
-
0x08fa4368
-
0x08fa2d50
-
0x08fa2d54
-
0x08fa2d58
-
- - 777e7d674d8908042307994cb75250ff -
0x09009860
-
0x08ff5a38
-
0x0900937C
-
0x08fe48e8
-
0x08ff58e0
-
0x08833324
-
0x089ee3d8
-
0x08FF578C
-
0x090093ec
-
0x0904bcac
-
0x09049cdc
-
0x0904bba4
-
0x08ff5764
-
0x08fe4900
-
0x09049d08
-
0x09049d0c
-
0x09049d10
-
0x0904b8e8
-
0x0904b87c
-
0x0904bcc4
-
0x0900c9b0
-
0x08833a40
-
0x0925a59c
-
0x089eecf4
-
0x089eecf8
-
0x089eecfc
-
0x0904b308
-
0x09049cf0
-
0x09049cf4
-
0x09049cf8
-
- - 04c3ad13c657f59ba6fc135e156d721d -
0x09008880
-
0x08ff4a58
-
0x0900839C
-
0x08fe3908
-
0x08ff4900
-
0x08832328
-
0x089ed3f8
-
0x08ff47ac
-
0x0900840c
-
0x0904accc
-
0x09048cfc
-
0x0904abc4
-
0x08ff4784
-
0xc60008fe
-
0x09048d28
-
0x09048d2c
-
0x09048d30
-
0x0904a908
-
0x0904a89c
-
0x0904ace4
-
0x0900b9d0
-
0x08832a60
-
0x091995fc
-
0x089edd14
-
0x089edd18
-
0x089edd1c
-
0x0904a328
-
0x09048d10
-
0x09048d14
-
0x09048d18
-
- - - 851c1190b6a7b42f2463967623d18575 - 0x4BB45F99 - - - 0x18 - 0x0 - 0xC - 0x1C - - -
0xe32798
-
0xe60838
-
0xe60814
-
0xae82cc
- -
0x17f5ab8
- Found addresses: (next to each other!) - 0x17f5ab8 - 0x17f5ac0 - 0x17f5ac8 - 0x17f5ad0 - -
0x146e45f
- Found addresses: - 0x146e45f - 0x185b677 - - Bogus:
0xae82cc
- -
0x017f6f38
- - Bogus:
0xae82cc
- - - -
0x016ad738
-
0x016ad73C
-
0x016ad740
- -
0x016ad744
-
0x016ad748
-
0x016ad74C
- -
0x016ad750
-
0x016ad754
-
0x016ad758
- -
0x016AD718
- 0x10 - 0x0092 - 0x029C - - - 0x0 - 0x1C - 0x38 - - - 0x0 - 0x90 - 0xF8 - 0xFC - 0x110 - 0x114 - 0x6D0 - 0x770 - - - - -
-
+ + +
From 5b8fa0ffa611cd6f1bf3993cd4ae9c555d5b179f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 2 Apr 2010 22:00:33 +0200 Subject: [PATCH 13/29] Added material addresses to veccheck and Memory.xml --- dfhack/DFHackAPI.cpp | 4 ++-- dfhack/DFVector.cpp | 2 +- examples/veccheck.cpp | 26 ++++++++++++++------------ output/Memory.xml | 31 ++++++++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/dfhack/DFHackAPI.cpp b/dfhack/DFHackAPI.cpp index fcaaf4db0..2d649c638 100644 --- a/dfhack/DFHackAPI.cpp +++ b/dfhack/DFHackAPI.cpp @@ -1079,8 +1079,8 @@ bool API::InitReadCreatures( uint32_t &numcreatures ) off.name_nickname_offset = minfo->getOffset("name_nickname"); off.name_words_offset = minfo->getOffset("name_words"); - // HACK: vector correction - off.vector_correct = minfo->getOffset("hacked_vector_start"); + // HACK: vector correction. No longer relevant. + off.vector_correct = 0; d->p_cre = new DfVector (d->p, off.creature_vector, 4); d->creaturesInited = true; diff --git a/dfhack/DFVector.cpp b/dfhack/DFVector.cpp index d03eabacb..4123940ca 100644 --- a/dfhack/DFVector.cpp +++ b/dfhack/DFVector.cpp @@ -33,7 +33,7 @@ DfVector::DfVector(Process * p, uint32_t address, uint32_t _item_size) uint32_t triplet[3]; item_size = _item_size; memory_info * mem = p->getDescriptor(); - uint32_t offs = mem->getOffset("hacked_vector_triplet"); + uint32_t offs = mem->getOffset("vector_triplet"); p->read(address + offs, sizeof(triplet), (uint8_t *) &triplet); start = triplet[0]; diff --git a/examples/veccheck.cpp b/examples/veccheck.cpp index 66c6431c5..ba82b2f87 100644 --- a/examples/veccheck.cpp +++ b/examples/veccheck.cpp @@ -64,29 +64,31 @@ int main (int numargs, const char ** args) DFHack::memory_info* mem = DF.getMemoryInfo(); //const vector * names = mem->getClassIDMapping(); - DumpObjStr0Vector("Inorganics",p,0x16afd04); + DumpObjStr0Vector("Material templates",p, mem->getAddress("mat_templates")); - DumpObjStr0Vector("Organics - all",p,0x16afd1C); + DumpObjStr0Vector("Inorganics",p, mem->getAddress("mat_inorganics")); - DumpObjStr0Vector("Organics - filtered",p,0x16afd34); + DumpObjStr0Vector("Organics - all",p, mem->getAddress("mat_organics_all")); - DumpDWordVector("Some weird numbers",p,0x16afd4C); + DumpObjStr0Vector("Organics - plants",p, mem->getAddress("mat_organics_plants")); - DumpObjStr0Vector("Trees/wood",p,0x16afd64); + DumpDWordVector("Maybe map between all organics and plants",p, mem->getAddress("mat_unk1_numbers")); - DumpDWordVector("More weird numbers",p,0x16afd7C); + DumpObjStr0Vector("Trees/wood",p, mem->getAddress("mat_organics_trees")); - DumpObjStr0Vector("WTF",p,0x16afd7C + 0x18 ); + DumpDWordVector("Maybe map between all organics and trees",p, mem->getAddress("mat_unk2_numbers")); - DumpObjStr0Vector("WTF2",p,0x16afd7C + 0x18 + 0x18); + DumpObjStr0Vector("Body material templates",p, mem->getAddress("mat_body_material_templates")); - DumpObjStr0Vector("WTF3",p,0x16afd7C + 0x18 + 0x18 + 0x18 ); + DumpObjStr0Vector("Body detail plans",p, mem->getAddress("mat_body_detail_plans")); - DumpObjStr0Vector("WTF4",p,0x16afd7C + 0x18 + 0x18 + 0x18 + 0x18); + DumpObjStr0Vector("Bodies",p, mem->getAddress("mat_bodies")); - DumpObjStr0Vector("WTF5",p,0x16afd7C + 0x18 + 0x18 + 0x18 + 0x18 + 0x18); + DumpObjStr0Vector("Bodygloss",p, mem->getAddress("mat_bodygloss")); - DumpObjStr0Vector("Creature types",p,0x16afd7C + 0x18 + 0x18 + 0x18 + 0x18 + 0x18 + 0x18); + DumpObjStr0Vector("Creature variations",p, mem->getAddress("mat_creature_variations")); + + DumpObjStr0Vector("Creature types",p, mem->getAddress("mat_creature_types")); #ifndef LINUX_BUILD diff --git a/output/Memory.xml b/output/Memory.xml index bba958a69..d39ba5400 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -210,6 +210,14 @@ ============ 0x18 0xC + Vector layout in MSVC 9: + DWORD Allocator + DWORD ? + DWORD ? + DWORD Start + DWORD End + DWORD AllocationEnd + 0x1C Position and window dimensions @@ -281,8 +289,27 @@ 0x6D0 0x770 + Materials + ========= + + +
0x16afcec
+
0x16afcec
+
0x16afd04
+
0x16afd1C
+
0x16afd34
+
0x16afd4C
+
0x16afd64
+
0x16afd7C
+
0x16AFD94
+
0x16AFDAC
+
0x16AFDC4
+
0x16AFDDC
+
0x16AFDF4
+
0x16AFE0C
+ + + From 6b42923f6b5b411204fcb3c0a5f89f37e41483ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 3 Apr 2010 00:04:38 +0200 Subject: [PATCH 14/29] Added back labors, base meta entry is connected to 0.31.01 --- output/Memory.xml | 283 +++++++++++++++++----------------------------- 1 file changed, 105 insertions(+), 178 deletions(-) diff --git a/output/Memory.xml b/output/Memory.xml index d39ba5400..1bcb1bc1e 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -1,184 +1,110 @@ - + - - - - - - - - 0x0 - + + + 0x0 + + ==================================================================== + L A B O R S + ==================================================================== + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 58 + 59 + 60 + 61 + 62 + 63 + 64 + 65 + + * Labor groups * + 4294967294 + 4294967293 + 4294967292 + 4294967291 + 4294967290 + 4294967289 + 4294967288 + 4294967287 + 4294967286 + 4294967285 + 4294967284 + 4294967283 + ==================================================================== + V -- T A B L E S + (for stonesense) + ==================================================================== + + + + + + - - .,:rsr, :2;,;r2A@@5 @@ -202,7 +128,7 @@ - + 851c1190b6a7b42f2463967623d18575 0x4BB45F99 @@ -280,6 +206,7 @@ Creatures ========= +
0x0166eccc
0x0 0x90 0xF8 From 7678ba7b633e36f2f056666a7914a55a90c34268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 3 Apr 2010 00:13:29 +0200 Subject: [PATCH 15/29] Added back skills --- output/Memory.xml | 111 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/output/Memory.xml b/output/Memory.xml index 1bcb1bc1e..d6ffb9ec2 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -10,6 +10,117 @@ 0 0 --> + ==================================================================== + S K I L L S + ==================================================================== + 0 + 1 + 2 + 49 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 22 + 23 + 57 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 24 + 25 + 26 + 67 + 68 + 66 + 65 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 86 + 87 + 88 + 89 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 99 + 100 + 101 + 102 + 103 + 104 + 105 + 38 + 39 + 41 + 42 + 43 + 44 + 50 + 52 + 45 + 46 + 47 + 48 + 70 + 36 + 12 + 69 + 37 + 13 + 14 + 16 + 17 + 18 + 53 + 54 + 55 + 56 + 40 + 51 + 21 + 15 + 58 + 19 + 20 + 106 + 59 + 60 + 61 + 62 + 63 + 64 + ==================================================================== L A B O R S ==================================================================== From ea6c5f726f723e2f7fe5306cbbc8f6f5971304f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 3 Apr 2010 00:44:17 +0200 Subject: [PATCH 16/29] More stuff! Ridiculous amounts of stuff! --- output/Memory.xml | 1533 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1530 insertions(+), 3 deletions(-) diff --git a/output/Memory.xml b/output/Memory.xml index d6ffb9ec2..16731cd91 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -4,12 +4,1538 @@ 0x0 + ==================================================================== + T R A I T S + ==================================================================== + ==================================================================== + P R O F E S S I O N S + ==================================================================== + TODO: Parse this and turn it into Profession tags. + Cross-reference with Memory-40d.xml + + ==================================================================== + J O B S + ==================================================================== + TODO: Parse this and turn it into Job tags + ==================================================================== S K I L L S ==================================================================== @@ -435,6 +1961,7 @@ +
From 265034b9d2ad64ac15ef1b908a4db14416c1fff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 3 Apr 2010 00:50:24 +0200 Subject: [PATCH 17/29] More stuff to parse. --- output/Memory.xml | 1285 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1285 insertions(+) diff --git a/output/Memory.xml b/output/Memory.xml index 16731cd91..412f73623 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -10,6 +10,1291 @@ + TODO: parse this, turn into Trait tags. Maybe most of it will be same as 40d. + ==================================================================== P R O F E S S I O N S ==================================================================== From 021fb1e0e9154392c62ebd31272a4e9210c92f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 3 Apr 2010 01:17:35 +0200 Subject: [PATCH 18/29] Small Memory.xml fixes --- output/Memory.xml | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/output/Memory.xml b/output/Memory.xml index 412f73623..b38aaa0c1 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -3082,6 +3082,8 @@ 0x17f5ac8 0x17f5ad0 + GUI State + =========
0x146e45f
Found addresses: 0x146e45f @@ -3120,7 +3122,7 @@
0x016ad754
0x016ad758
- name struct + Name struct =========== 0x0 0x1C @@ -3128,7 +3130,7 @@ Creatures ========= -
0x0166eccc
+
0x0166ecc4
0x0 0x90 0xF8 @@ -3137,24 +3139,42 @@ 0x114 0x6D0 0x770 + 0x830 Materials ========= +
0x16afcec
0x16afcec
+ +
0x16afd04
+ +
0x16afd1C
+ +
0x16afd34
-
0x16afd4C
+ + +
0x16afd4C
+ +
0x16afd64
-
0x16afd7C
+ + +
0x16afd7C
+ +
0x16AFD94
0x16AFDAC
0x16AFDC4
0x16AFDDC
0x16AFDF4
+ +
0x16AFE0C
SHM initialization (if possible) <-- - */ - g_pProcess->getModuleIndex("Maps",3,d->maps_module); - - if(d->maps_module) + // detach all processes, destroy manager + if (d->pm == 0) { - // supply the module with offsets so it can work with them - Maps::maps_offsets *off = SHMDATA(Maps::maps_offsets); - off->biome_stuffs = d->biome_stuffs; - off->designation_offset = d->designation_offset; - off->map_offset = map_offset; - off->occupancy_offset = d->occupancy_offset; - off->tile_type_offset = d->tile_type_offset; - off->vein_ice_vptr = d->vein_ice_vptr; // FIXME: not necessarily true, the shm server side needs a class lookup >_< - off->vein_mineral_vptr = d->vein_mineral_vptr; // FIXME: not necessarily true, the shm server side needs a class lookup >_< - off->veinvector = d->veinvector; - off->x_count_offset = x_count_offset; - off->y_count_offset = y_count_offset; - off->z_count_offset = z_count_offset; - full_barrier - const uint32_t cmd = Maps::MAP_INIT + (d->maps_module << 16); - g_pProcess->SetAndWait(cmd); - //cerr << "Map acceleration enabled!" << endl; + d->pm = new ProcessEnumerator (d->xml); // FIXME: handle bad XML better } - - // get the map pointer - uint32_t x_array_loc = g_pProcess->readDWord (map_offset); - if (!x_array_loc) + else { - return false; - // FIXME: only throw this due to programmer error, in the other map functions - //throw Error::NoMapLoaded(); + d->pm->purge(); } - - // get the size - uint32_t mx, my, mz; - mx = d->x_block_count = g_pProcess->readDWord (x_count_offset); - my = d->y_block_count = g_pProcess->readDWord (y_count_offset); - mz = d->z_block_count = g_pProcess->readDWord (z_count_offset); - - // test for wrong map dimensions - if (mx == 0 || mx > 48 || my == 0 || my > 48 || mz == 0) + + // find a process (ProcessManager can find multiple when used properly) + if (!d->pm->findProcessess()) { - throw Error::BadMapDimensions(mx, my); + throw Error::NoProcess(); + //cerr << "couldn't find a suitable process" << endl; //return false; } - - // alloc array for pointers to all blocks - d->block = new uint32_t[mx*my*mz]; - uint32_t *temp_x = new uint32_t[mx]; - uint32_t *temp_y = new uint32_t[my]; - uint32_t *temp_z = new uint32_t[mz]; - - g_pProcess->read (x_array_loc, mx * sizeof (uint32_t), (uint8_t *) temp_x); - for (uint32_t x = 0; x < mx; x++) + d->p = (*d->pm) [0]; + if (!d->p->attach()) { - g_pProcess->read (temp_x[x], my * sizeof (uint32_t), (uint8_t *) temp_y); - // y -> map column - for (uint32_t y = 0; y < my; y++) - { - g_pProcess->read (temp_y[y], - mz * sizeof (uint32_t), - (uint8_t *) (d->block + x*my*mz + y*mz)); - } + throw Error::CantAttach(); + //cerr << "couldn't attach to process" << endl; + //return false; // couldn't attach to process, no go } - delete [] temp_x; - delete [] temp_y; - delete [] temp_z; + d->shm_start = d->p->getSHMStart(); + d->offset_descriptor = d->p->getDescriptor(); + // process is attached, everything went just fine... hopefully return true; } -bool API::DestroyMap() -{ - if (d->block != NULL) - { - delete [] d->block; - d->block = NULL; - } - return true; -} -bool API::isValidBlock (uint32_t x, uint32_t y, uint32_t z) +bool API::Detach() { - if ( x >= d->x_block_count || y >= d->y_block_count || z >= d->z_block_count) + if(!d->p) return false; - return d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z] != 0; -} - -uint32_t API::getBlockPtr (uint32_t x, uint32_t y, uint32_t z) -{ - if ( x >= d->x_block_count || y >= d->y_block_count || z >= d->z_block_count) - return 0; - return d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; -} - -bool API::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer) -{ - if(d->shm_start && d->maps_module) // ACCELERATE! - { - SHMMAPSHDR->x = x; - SHMMAPSHDR->y = y; - SHMMAPSHDR->z = z; - volatile uint32_t cmd = Maps::MAP_READ_BLOCK_BY_COORDS + (d->maps_module << 16); - if(!g_pProcess->SetAndWait(cmd)) - return false; - memcpy(buffer,SHMDATA(mapblock40d),sizeof(mapblock40d)); - return true; - } - else // plain old block read + if (!d->p->detach()) { - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - g_pProcess->read (addr + d->tile_type_offset, sizeof (buffer->tiletypes), (uint8_t *) buffer->tiletypes); - g_pProcess->read (addr + d->occupancy_offset, sizeof (buffer->occupancy), (uint8_t *) buffer->occupancy); - g_pProcess->read (addr + d->designation_offset, sizeof (buffer->designation), (uint8_t *) buffer->designation); - g_pProcess->read (addr + d->biome_stuffs, sizeof (buffer->biome_indices), (uint8_t *) buffer->biome_indices); - buffer->origin = addr; - uint32_t addr_of_struct = g_pProcess->readDWord(addr); - buffer->blockflags.whole = g_pProcess->readDWord(addr_of_struct); - return true; - } return false; } -} - - -// 256 * sizeof(uint16_t) -bool API::ReadTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - g_pProcess->read (addr + d->tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); - return true; - } - return false; -} - -bool API::ReadDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool &dirtybit) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if(addr) - { - uint32_t addr_of_struct = g_pProcess->readDWord(addr); - dirtybit = g_pProcess->readDWord(addr_of_struct) & 1; - return true; - } - return false; -} - -bool API::WriteDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool dirtybit) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - uint32_t addr_of_struct = g_pProcess->readDWord(addr); - uint32_t dirtydword = g_pProcess->readDWord(addr_of_struct); - dirtydword &= 0xFFFFFFFE; - dirtydword |= (uint32_t) dirtybit; - g_pProcess->writeDWord (addr_of_struct, dirtydword); - return true; - } - return false; -} - -/// read/write the block flags -bool API::ReadBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags &blockflags) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if(addr) - { - uint32_t addr_of_struct = g_pProcess->readDWord(addr); - blockflags.whole = g_pProcess->readDWord(addr_of_struct); - return true; - } - return false; -} -bool API::WriteBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags blockflags) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - uint32_t addr_of_struct = g_pProcess->readDWord(addr); - g_pProcess->writeDWord (addr_of_struct, blockflags.whole); - return true; - } - return false; -} - -bool API::ReadDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - g_pProcess->read (addr + d->designation_offset, sizeof (designations40d), (uint8_t *) buffer); - return true; - } - return false; -} - - -// 256 * sizeof(uint32_t) -bool API::ReadOccupancy (uint32_t x, uint32_t y, uint32_t z, occupancies40d *buffer) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - g_pProcess->read (addr + d->occupancy_offset, sizeof (occupancies40d), (uint8_t *) buffer); - return true; - } - return false; -} - - -// 256 * sizeof(uint16_t) -bool API::WriteTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) + if (d->pm != NULL) { - g_pProcess->write (addr + d->tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); - return true; + delete d->pm; } - return false; -} - -bool API::getCurrentCursorCreature(uint32_t & creature_index) -{ - if(!d->cursorWindowInited) return false; - creature_index = g_pProcess->readDWord(d->current_cursor_creature_offset); + d->pm = NULL; + d->p = NULL; + d->shm_start = 0; + d->offset_descriptor = NULL; return true; } -// 256 * sizeof(uint32_t) -bool API::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer) -{ - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - g_pProcess->write (addr + d->designation_offset, sizeof (designations40d), (uint8_t *) buffer); - return true; - } - return false; -} -// 256 * sizeof(uint32_t) -bool API::WriteOccupancy (uint32_t x, uint32_t y, uint32_t z, occupancies40d *buffer) +bool API::isAttached() { - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - g_pProcess->write (addr + d->occupancy_offset, sizeof (occupancies40d), (uint8_t *) buffer); - return true; - } - return false; + return d->p != NULL; } -// FIXME: this is bad. determine the real size! -//16 of them? IDK... there's probably just 7. Reading more doesn't cause errors as it's an array nested inside a block -// 16 * sizeof(uint8_t) -bool API::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40d *buffer) +bool API::Suspend() { - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - if (addr) - { - g_pProcess->read (addr + d->biome_stuffs, sizeof (biome_indices40d), (uint8_t *) buffer); - return true; - } - return false; + return d->p->suspend(); } - -// veins of a block, expects empty vein vectors -bool API::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector & veins, vector & ices) +bool API::AsyncSuspend() { - uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; - veins.clear(); - ices.clear(); - if (addr && d->veinvector && d->veinsize) - { - // veins are stored as a vector of pointers to veins - /*pointer is 4 bytes! we work with a 32bit program here, no matter what architecture we compile khazad for*/ - DfVector p_veins (d->p, addr + d->veinvector, 4); - uint32_t size = p_veins.getSize(); - veins.reserve (size); - - // read all veins - for (uint32_t i = 0; i < size;i++) - { - t_vein v; - t_frozenliquidvein fv; - - // read the vein pointer from the vector - uint32_t temp = * (uint32_t *) p_veins[i]; - uint32_t type = g_pProcess->readDWord(temp); -try_again: - if(type == d->vein_mineral_vptr) - { - // read the vein data (dereference pointer) - g_pProcess->read (temp, sizeof(t_vein), (uint8_t *) &v); - v.address_of = temp; - // store it in the vector - veins.push_back (v); - } - else if(type == d->vein_ice_vptr) - { - // read the ice vein data (dereference pointer) - g_pProcess->read (temp, sizeof(t_frozenliquidvein), (uint8_t *) &fv); - // store it in the vector - ices.push_back (fv); - } - else if(g_pProcess->readClassName(type) == "block_square_event_frozen_liquid") - { - d->vein_ice_vptr = type; - goto try_again; - } - else if(g_pProcess->readClassName(type) == "block_square_event_mineral") - { - d->vein_mineral_vptr = type; - goto try_again; - } - } - return true; - } - return false; + return d->p->asyncSuspend(); } - -// getter for map size -void API::getSize (uint32_t& x, uint32_t& y, uint32_t& z) +bool API::Resume() { - x = d->x_block_count; - y = d->y_block_count; - z = d->z_block_count; + return d->p->resume(); } - -bool API::ReadWoodMatgloss (vector & woods) +bool API::ForceResume() { - - int matgloss_address = d->offset_descriptor->getAddress ("matgloss"); - int matgloss_wood_name_offset = d->offset_descriptor->getOffset("matgloss_wood_name"); - // TODO: find flag for autumnal coloring? - DfVector p_matgloss(d->p, matgloss_address, 4); - - woods.clear(); - - t_matgloss mat; - // TODO: use brown? - mat.fore = 7; - mat.back = 0; - mat.bright = 0; - uint32_t size = p_matgloss.getSize(); - for (uint32_t i = 0; i < size ;i++) - { - // read the matgloss pointer from the vector into temp - uint32_t temp = * (uint32_t *) p_matgloss[i]; - // read the string pointed at by - /* - fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address - */ - d->p->readSTLString (temp, mat.id, 128); - d->p->readSTLString (temp+matgloss_wood_name_offset, mat.name, 128); - woods.push_back (mat); - } - return true; + return d->p->forceresume(); } - -bool API::ReadStoneMatgloss (vector & stones) +bool API::isSuspended() { - memory_info * minfo = d->offset_descriptor; - int matgloss_address = minfo->getAddress ("matgloss"); - int matgloss_offset = minfo->getHexValue ("matgloss_skip"); - int matgloss_colors = minfo->getOffset ("matgloss_stone_color"); - int matgloss_stone_name_offset = minfo->getOffset("matgloss_stone_name"); - - DfVector p_matgloss (d->p, matgloss_address + matgloss_offset, 4); - - uint32_t size = p_matgloss.getSize(); - stones.resize (0); - stones.reserve (size); - for (uint32_t i = 0; i < size;i++) - { - // read the matgloss pointer from the vector into temp - uint32_t temp = * (uint32_t *) p_matgloss[i]; - // read the string pointed at by - t_matgloss mat; - //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address - d->p->readSTLString (temp, mat.id, 128); - d->p->readSTLString (temp+matgloss_stone_name_offset, mat.name, 128); - mat.fore = (uint8_t) g_pProcess->readWord (temp + matgloss_colors); - mat.back = (uint8_t) g_pProcess->readWord (temp + matgloss_colors + 2); - mat.bright = (uint8_t) g_pProcess->readWord (temp + matgloss_colors + 4); - stones.push_back (mat); - } - return true; + return d->p->isSuspended(); } - -bool API::ReadMetalMatgloss (vector & metals) +void API::ReadRaw (const uint32_t offset, const uint32_t size, uint8_t *target) { - memory_info * minfo = d->offset_descriptor; - int matgloss_address = minfo->getAddress ("matgloss"); - int matgloss_offset = minfo->getHexValue ("matgloss_skip"); - int matgloss_colors = minfo->getOffset ("matgloss_metal_color"); - int matgloss_metal_name_offset = minfo->getOffset("matgloss_metal_name"); - DfVector p_matgloss(d->p, matgloss_address + matgloss_offset * 3, 4); - - metals.clear(); - - for (uint32_t i = 0; i < p_matgloss.getSize();i++) - { - // read the matgloss pointer from the vector into temp - uint32_t temp = * (uint32_t *) p_matgloss[i]; - // read the string pointed at by - t_matgloss mat; - //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address - d->p->readSTLString (temp, mat.id, 128); - d->p->readSTLString (temp+matgloss_metal_name_offset, mat.name, 128); - mat.fore = (uint8_t) g_pProcess->readWord (temp + matgloss_colors); - mat.back = (uint8_t) g_pProcess->readWord (temp + matgloss_colors + 2); - mat.bright = (uint8_t) g_pProcess->readWord (temp + matgloss_colors + 4); - metals.push_back (mat); - } - return true; + g_pProcess->read (offset, size, target); } -bool API::ReadPlantMatgloss (vector & plants) +void API::WriteRaw (const uint32_t offset, const uint32_t size, uint8_t *source) { - memory_info * minfo = d->offset_descriptor; - int matgloss_address = minfo->getAddress ("matgloss"); - int matgloss_offset = minfo->getHexValue ("matgloss_skip"); - int matgloss_plant_name_offset = minfo->getOffset("matgloss_plant_name"); - DfVector p_matgloss(d->p, matgloss_address + matgloss_offset * 2, 4); - - plants.clear(); - - // TODO: use green? - t_matgloss mat; - mat.fore = 7; - mat.back = 0; - mat.bright = 0; - for (uint32_t i = 0; i < p_matgloss.getSize();i++) - { - // read the matgloss pointer from the vector into temp - uint32_t temp = * (uint32_t *) p_matgloss[i]; - // read the string pointed at by - //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address - d->p->readSTLString (temp, mat.id, 128); - d->p->readSTLString (temp+matgloss_plant_name_offset, mat.name, 128); - plants.push_back (mat); - } - return true; + g_pProcess->write (offset, size, source); } -bool API::ReadPlantMatgloss (vector & plants) +memory_info *API::getMemoryInfo() { - memory_info * minfo = d->offset_descriptor; - int matgloss_address = minfo->getAddress ("matgloss"); - int matgloss_offset = minfo->getHexValue ("matgloss_skip"); - int matgloss_plant_name_offset = minfo->getOffset("matgloss_plant_name"); - int matgloss_plant_drink_offset = minfo->getOffset("matgloss_plant_drink"); - int matgloss_plant_food_offset = minfo->getOffset("matgloss_plant_food"); - int matgloss_plant_extract_offset = minfo->getOffset("matgloss_plant_extract"); - DfVector p_matgloss(d->p, matgloss_address + matgloss_offset * 2, 4); - - plants.clear(); - - // TODO: use green? - t_matglossPlant mat; - mat.fore = 7; - mat.back = 0; - mat.bright = 0; - for (uint32_t i = 0; i < p_matgloss.getSize();i++) - { - // read the matgloss pointer from the vector into temp - uint32_t temp = * (uint32_t *) p_matgloss[i]; - // read the string pointed at by - //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address - d->p->readSTLString (temp, mat.id, 128); - d->p->readSTLString (temp+matgloss_plant_name_offset, mat.name, 128); - d->p->readSTLString (temp+matgloss_plant_drink_offset, mat.drink_name, 128); - d->p->readSTLString (temp+matgloss_plant_food_offset, mat.food_name, 128); - d->p->readSTLString (temp+matgloss_plant_extract_offset, mat.extract_name, 128); - - //d->p->readSTLString (temp - plants.push_back (mat); - } - return true; + return d->offset_descriptor; } - -bool API::ReadCreatureMatgloss (vector & creatures) +Process * API::getProcess() { - memory_info * minfo = d->offset_descriptor; - int matgloss_address = minfo->getAddress ("matgloss"); - int matgloss_offset = minfo->getHexValue ("matgloss_skip"); - int matgloss_creature_name_offset = minfo->getOffset("matgloss_creature_name"); - DfVector p_matgloss (d->p, matgloss_address + matgloss_offset * 6, 4); - - creatures.clear(); - - // TODO: use green? - t_matgloss mat; - mat.fore = 7; - mat.back = 0; - mat.bright = 0; - for (uint32_t i = 0; i < p_matgloss.getSize();i++) - { - // read the matgloss pointer from the vector into temp - uint32_t temp = * (uint32_t *) p_matgloss[i]; - // read the string pointed at by - //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address - d->p->readSTLString (temp, mat.id, 128); - d->p->readSTLString (temp+matgloss_creature_name_offset, mat.name, 128); - creatures.push_back (mat); - } - return true; + return d->p; } - -//vector v_geology[eBiomeCount]; -bool API::ReadGeology (vector < vector >& assign) +DFWindow * API::getWindow() { - memory_info * minfo = d->offset_descriptor; - // get needed addresses and offsets. Now this is what I call crazy. - int region_x_offset = minfo->getAddress ("region_x"); - int region_y_offset = minfo->getAddress ("region_y"); - int region_z_offset = minfo->getAddress ("region_z"); - int world_offset = minfo->getAddress ("world"); - int world_regions_offset = minfo->getOffset ("w_regions_arr"); - int region_size = minfo->getHexValue ("region_size"); - int region_geo_index_offset = minfo->getOffset ("region_geo_index_off"); - int world_geoblocks_offset = minfo->getOffset ("w_geoblocks"); - int world_size_x = minfo->getOffset ("world_size_x"); - int world_size_y = minfo->getOffset ("world_size_y"); - int geolayer_geoblock_offset = minfo->getOffset ("geolayer_geoblock_offset"); - - uint32_t regionX, regionY, regionZ; - uint16_t worldSizeX, worldSizeY; - - // read position of the region inside DF world - g_pProcess->readDWord (region_x_offset, regionX); - g_pProcess->readDWord (region_y_offset, regionY); - g_pProcess->readDWord (region_z_offset, regionZ); - - // get world size - g_pProcess->readWord (world_offset + world_size_x, worldSizeX); - g_pProcess->readWord (world_offset + world_size_y, worldSizeY); - - // get pointer to first part of 2d array of regions - uint32_t regions = g_pProcess->readDWord (world_offset + world_regions_offset); - - // read the geoblock vector - DfVector geoblocks (d->p, world_offset + world_geoblocks_offset, 4); - - // iterate over 8 surrounding regions + local region - for (int i = eNorthWest; i < eBiomeCount; i++) - { - // check bounds, fix them if needed - int bioRX = regionX / 16 + (i % 3) - 1; - if (bioRX < 0) bioRX = 0; - if (bioRX >= worldSizeX) bioRX = worldSizeX - 1; - int bioRY = regionY / 16 + (i / 3) - 1; - if (bioRY < 0) bioRY = 0; - if (bioRY >= worldSizeY) bioRY = worldSizeY - 1; - - // get pointer to column of regions - uint32_t geoX; - g_pProcess->readDWord (regions + bioRX*4, geoX); - - // get index into geoblock vector - uint16_t geoindex; - g_pProcess->readWord (geoX + bioRY*region_size + region_geo_index_offset, geoindex); - - // get the geoblock from the geoblock vector using the geoindex - // read the matgloss pointer from the vector into temp - uint32_t geoblock_off = * (uint32_t *) geoblocks[geoindex]; - - // get the vector with pointer to layers - DfVector geolayers (d->p, geoblock_off + geolayer_geoblock_offset , 4); // let's hope - // make sure we don't load crap - assert (geolayers.getSize() > 0 && geolayers.getSize() <= 16); - - d->v_geology[i].reserve (geolayers.getSize()); - // finally, read the layer matgloss - for (uint32_t j = 0;j < geolayers.getSize();j++) - { - // read pointer to a layer - uint32_t geol_offset = * (uint32_t *) geolayers[j]; - // read word at pointer + 2, store in our geology vectors - d->v_geology[i].push_back (g_pProcess->readWord (geol_offset + 2)); - } - } - assign.clear(); - assign.reserve (eBiomeCount); -// // TODO: clean this up - for (int i = 0; i < eBiomeCount;i++) - { - assign.push_back (d->v_geology[i]); - } - return true; + return d->p->getWindow(); } - +/* // returns number of buildings, expects v_buildingtypes that will later map t_building.type to its name bool API::InitReadBuildings ( uint32_t& numbuildings ) { @@ -1034,86 +366,8 @@ void API::FinishReadVegetation() } d->vegetationInited = false; } - - -bool API::InitReadCreatures( uint32_t &numcreatures ) -{ - if(!d->InitReadNames()) return false; - try - { - memory_info * minfo = d->offset_descriptor; - Creatures::creature_offsets & off = d->creatures; - off.creature_vector = minfo->getAddress ("creatures"); - off.creature_pos_offset = minfo->getOffset ("creature_position"); - off.creature_type_offset = minfo->getOffset ("creature_race"); - off.creature_flags1_offset = minfo->getOffset ("creature_flags1"); - off.creature_flags2_offset = minfo->getOffset ("creature_flags2"); - off.creature_name_offset = minfo->getOffset ("creature_name"); - off.creature_custom_profession_offset = minfo->getOffset ("creature_custom_profession"); - off.creature_profession_offset = minfo->getOffset ("creature_profession"); - off.creature_sex_offset = minfo->getOffset ("creature_sex"); - off.creature_id_offset = minfo->getOffset ("creature_id"); - off.creature_squad_name_offset = minfo->getOffset ("creature_squad_name"); - off.creature_squad_leader_id_offset = minfo->getOffset ("creature_squad_leader_id"); - off.creature_money_offset = minfo->getOffset ("creature_money"); - off.creature_current_job_offset = minfo->getOffset ("creature_current_job"); - off.creature_current_job_id_offset = minfo->getOffset ("current_job_id"); - off.creature_strength_offset = minfo->getOffset ("creature_strength"); - off.creature_agility_offset = minfo->getOffset ("creature_agility"); - off.creature_toughness_offset = minfo->getOffset ("creature_toughness"); - off.creature_skills_offset = minfo->getOffset ("creature_skills"); - off.creature_labors_offset = minfo->getOffset ("creature_labors"); - off.creature_happiness_offset = minfo->getOffset ("creature_happiness"); - off.creature_traits_offset = minfo->getOffset ("creature_traits"); - off.creature_likes_offset = minfo->getOffset("creature_likes"); - off.creature_artifact_name_offset = minfo->getOffset("creature_artifact_name"); - off.creature_mood_offset = minfo->getOffset("creature_mood"); - - off.creature_pregnancy_offset = minfo->getOffset("creature_pregnancy"); - off.creature_blood_max_offset = minfo->getOffset("creature_blood_max"); - off.creature_blood_current_offset = minfo->getOffset("creature_blood_current"); - off.creature_bleed_offset = minfo->getOffset("creature_bleed"); - - // name offsets for the creature module - off.name_firstname_offset = minfo->getOffset("name_firstname"); - off.name_nickname_offset = minfo->getOffset("name_nickname"); - off.name_words_offset = minfo->getOffset("name_words"); - - // HACK: vector correction. No longer relevant. - off.vector_correct = 0; - - d->p_cre = new DfVector (d->p, off.creature_vector, 4); - d->creaturesInited = true; - numcreatures = d->p_cre->getSize(); - - /* - * --> SHM initialization (if possible) <-- - */ - g_pProcess->getModuleIndex("Creatures40d",1,d->creature_module); - - if(d->creature_module) - { - // supply the module with offsets so it can work with them - memcpy(SHMDATA(Creatures::creature_offsets),&d->creatures,sizeof(Creatures::creature_offsets)); - const uint32_t cmd = Creatures::CREATURE_INIT + (d->creature_module << 16); - g_pProcess->SetAndWait(cmd); - //cerr << "Creature acceleration enabled!" << endl; - } - /* - else - { - cerr << "Creature acceleration NOT enabled!" << endl; - } - */ - return true; - } - catch (Error::MissingMemoryDefinition&) - { - d->creaturesInited = false; - numcreatures = 0; - throw; - } -} +*/ +/* bool API::InitReadNotes( uint32_t &numnotes ) { try @@ -1328,104 +582,8 @@ bool API::getItemIndexesInBox(vector &indexes, } return true; } - -bool API::ReadCreature (const int32_t index, t_creature & furball) -{ - if(!d->creaturesInited) return false; - if(d->creature_module) - { - // supply the module with offsets so it can work with them - SHMCREATURESHDR->index = index; - const uint32_t cmd = Creatures::CREATURE_AT_INDEX + (d->creature_module << 16); - g_pProcess->SetAndWait(cmd); - memcpy(&furball,SHMDATA(t_creature),sizeof(t_creature)); - // cerr << "creature read from SHM!" << endl; - return true; - } - // read pointer from vector at position - uint32_t temp = * (uint32_t *) d->p_cre->at (index); - furball.origin = temp; - Creatures::creature_offsets &offs = d->creatures; - //read creature from memory - g_pProcess->read (temp + offs.creature_pos_offset, 3 * sizeof (uint16_t), (uint8_t *) & (furball.x)); // xyz really - g_pProcess->readDWord (temp + offs.creature_type_offset, furball.type); - g_pProcess->readDWord (temp + offs.creature_flags1_offset, furball.flags1.whole); - g_pProcess->readDWord (temp + offs.creature_flags2_offset, furball.flags2.whole); - // names - d->readName(furball.name,temp + offs.creature_name_offset); - d->readName(furball.squad_name, temp + offs.creature_squad_name_offset); - d->readName(furball.artifact_name, temp + offs.creature_artifact_name_offset); - // custom profession - fill_char_buf (furball.custom_profession, d->p->readSTLString (temp + offs.creature_custom_profession_offset)); - - // labors - g_pProcess->read (temp + offs.creature_labors_offset, NUM_CREATURE_LABORS, furball.labors); - // traits - g_pProcess->read (temp + offs.creature_traits_offset, sizeof (uint16_t) * NUM_CREATURE_TRAITS, (uint8_t *) &furball.traits); - // learned skills - DfVector skills (d->p, temp + offs.creature_skills_offset, 4 ); - furball.numSkills = skills.getSize(); - for (uint32_t i = 0; i < furball.numSkills;i++) - { - uint32_t temp2 = * (uint32_t *) skills[i]; - //skills.read(i, (uint8_t *) &temp2); - // a byte: this gives us 256 skills maximum. - furball.skills[i].id = g_pProcess->readByte (temp2); - furball.skills[i].rating = g_pProcess->readByte (temp2 + 4); - furball.skills[i].experience = g_pProcess->readWord (temp2 + 8); - } - // profession - furball.profession = g_pProcess->readByte (temp + offs.creature_profession_offset); - // current job HACK: the job object isn't cleanly represented here - uint32_t jobIdAddr = g_pProcess->readDWord (temp + offs.creature_current_job_offset); - - if (jobIdAddr) - { - furball.current_job.active = true; - furball.current_job.jobId = g_pProcess->readByte (jobIdAddr + offs.creature_current_job_id_offset); - } - else - { - furball.current_job.active = false; - } - - //likes - DfVector likes(d->p, temp + offs.creature_likes_offset, 4); - furball.numLikes = likes.getSize(); - for(uint32_t i = 0;iread(temp2,sizeof(t_like),(uint8_t *) &furball.likes[i]); - } - - furball.mood = (int16_t) g_pProcess->readWord (temp + offs.creature_mood_offset); - - - g_pProcess->readDWord (temp + offs.creature_happiness_offset, furball.happiness); - g_pProcess->readDWord (temp + offs.creature_id_offset, furball.id); - g_pProcess->readDWord (temp + offs.creature_agility_offset, furball.agility); - g_pProcess->readDWord (temp + offs.creature_strength_offset, furball.strength); - g_pProcess->readDWord (temp + offs.creature_toughness_offset, furball.toughness); - g_pProcess->readDWord (temp + offs.creature_money_offset, furball.money); - furball.squad_leader_id = (int32_t) g_pProcess->readDWord (temp + offs.creature_squad_leader_id_offset); - g_pProcess->readByte (temp + offs.creature_sex_offset, furball.sex); - - g_pProcess->readDWord(temp + offs.creature_pregnancy_offset, furball.pregnancy_timer); - furball.blood_max = (int32_t) g_pProcess->readDWord(temp + offs.creature_blood_max_offset); - furball.blood_current = (int32_t) g_pProcess->readDWord(temp + offs.creature_blood_current_offset); - g_pProcess->readDWord(temp + offs.creature_bleed_offset, furball.bleed_rate); - - - return true; -} - -bool API::WriteLabors(const uint32_t index, uint8_t labors[NUM_CREATURE_LABORS]) -{ - if(!d->creaturesInited) return false; - uint32_t temp = * (uint32_t *) d->p_cre->at (index); - WriteRaw(temp + d->creatures.creature_labors_offset, NUM_CREATURE_LABORS, labors); -} - +*/ +/* bool API::InitReadNameTables(vector > & translations , vector > & foreign_languages) //(map< string, vector > & nameTable) { try @@ -1569,208 +727,9 @@ void API::FinishReadNotes() } d->notesInited = false; } - -bool API::Attach() -{ - // detach all processes, destroy manager - if (d->pm == 0) - { - d->pm = new ProcessEnumerator (d->xml); // FIXME: handle bad XML better - } - else - { - d->pm->purge(); - } - - // find a process (ProcessManager can find multiple when used properly) - if (!d->pm->findProcessess()) - { - throw Error::NoProcess(); - //cerr << "couldn't find a suitable process" << endl; - //return false; - } - d->p = (*d->pm) [0]; - if (!d->p->attach()) - { - throw Error::CantAttach(); - //cerr << "couldn't attach to process" << endl; - //return false; // couldn't attach to process, no go - } - d->shm_start = d->p->getSHMStart(); - d->offset_descriptor = d->p->getDescriptor(); - // process is attached, everything went just fine... hopefully - return true; -} - - -bool API::Detach() -{ - if(!d->p) - return false; - if (!d->p->detach()) - { - return false; - } - if (d->pm != NULL) - { - delete d->pm; - } - d->pm = NULL; - d->p = NULL; - d->shm_start = 0; - d->offset_descriptor = NULL; - return true; -} - -bool API::isAttached() -{ - return d->p != NULL; -} - -bool API::Suspend() -{ - return d->p->suspend(); -} -bool API::AsyncSuspend() -{ - return d->p->asyncSuspend(); -} - -bool API::Resume() -{ - return d->p->resume(); -} -bool API::ForceResume() -{ - return d->p->forceresume(); -} -bool API::isSuspended() -{ - return d->p->isSuspended(); -} - -void API::ReadRaw (const uint32_t offset, const uint32_t size, uint8_t *target) -{ - g_pProcess->read (offset, size, target); -} - -void API::WriteRaw (const uint32_t offset, const uint32_t size, uint8_t *source) -{ - g_pProcess->write (offset, size, source); -} - -bool API::InitViewAndCursor() -{ - try - { - d->window_x_offset = d->offset_descriptor->getAddress ("window_x"); - d->window_y_offset = d->offset_descriptor->getAddress ("window_y"); - d->window_z_offset = d->offset_descriptor->getAddress ("window_z"); - d->cursor_xyz_offset = d->offset_descriptor->getAddress ("cursor_xyz"); - d->current_cursor_creature_offset = d->offset_descriptor->getAddress ("current_cursor_creature"); - - d->current_menu_state_offset = d->offset_descriptor->getAddress("current_menu_state"); - d->pause_state_offset = d->offset_descriptor->getAddress ("pause_state"); - d->view_screen_offset = d->offset_descriptor->getAddress ("view_screen"); - - d->cursorWindowInited = true; - return true; - } - catch (Error::MissingMemoryDefinition&) - { - d->cursorWindowInited = false; - throw; - } -} - -bool API::InitViewSize() -{ - try - { - d->window_dims_offset = d->offset_descriptor->getAddress ("window_dims"); - - d->viewSizeInited = true; - return true; - } - catch (Error::MissingMemoryDefinition&) - { - d->viewSizeInited = false; - throw; - } -} - -bool API::getViewCoords (int32_t &x, int32_t &y, int32_t &z) -{ - if (!d->cursorWindowInited) return false; - g_pProcess->readDWord (d->window_x_offset, (uint32_t &) x); - g_pProcess->readDWord (d->window_y_offset, (uint32_t &) y); - g_pProcess->readDWord (d->window_z_offset, (uint32_t &) z); - return true; -} -//FIXME: confine writing of coords to map bounds? -bool API::setViewCoords (const int32_t x, const int32_t y, const int32_t z) -{ - if (!d->cursorWindowInited) return false; - g_pProcess->writeDWord (d->window_x_offset, (uint32_t) x); - g_pProcess->writeDWord (d->window_y_offset, (uint32_t) y); - g_pProcess->writeDWord (d->window_z_offset, (uint32_t) z); - return true; -} - -bool API::getCursorCoords (int32_t &x, int32_t &y, int32_t &z) -{ - if(!d->cursorWindowInited) return false; - int32_t coords[3]; - g_pProcess->read (d->cursor_xyz_offset, 3*sizeof (int32_t), (uint8_t *) coords); - x = coords[0]; - y = coords[1]; - z = coords[2]; - if (x == -30000) return false; - return true; -} -//FIXME: confine writing of coords to map bounds? -bool API::setCursorCoords (const int32_t x, const int32_t y, const int32_t z) -{ - if (!d->cursorWindowInited) return false; - int32_t coords[3] = {x, y, z}; - g_pProcess->write (d->cursor_xyz_offset, 3*sizeof (int32_t), (uint8_t *) coords); - return true; -} -bool API::getWindowSize (int32_t &width, int32_t &height) -{ - if(! d->viewSizeInited) return false; - - int32_t coords[2]; - g_pProcess->read (d->window_dims_offset, 2*sizeof (int32_t), (uint8_t *) coords); - width = coords[0]; - height = coords[1]; - return true; -} -/* -bool API::getClassIDMapping (vector & objecttypes) -{ - if(isAttached()) - { - d->offset_descriptor->getClassIDMapping(objecttypes); - return true; - } - return false; -} */ -memory_info *API::getMemoryInfo() -{ - return d->offset_descriptor; -} -Process * API::getProcess() -{ - return d->p; -} - -DFWindow * API::getWindow() -{ - return d->p->getWindow(); -} +/* bool API::InitReadItems(uint32_t & numitems) { try @@ -1831,39 +790,8 @@ void API::FinishReadItems() } d->itemsInited = false; } - -bool API::ReadPauseState() -{ - // replace with an exception - if(!d->cursorWindowInited) return false; - - uint32_t pauseState = g_pProcess->readDWord (d->pause_state_offset); - return pauseState & 1; -} - -uint32_t API::ReadMenuState() -{ - if(d->cursorWindowInited) - return(g_pProcess->readDWord(d->current_menu_state_offset)); - return false; -} - -bool API::ReadViewScreen (t_viewscreen &screen) -{ - if (!d->cursorWindowInited) return false; - - uint32_t last = g_pProcess->readDWord (d->view_screen_offset); - uint32_t screenAddr = g_pProcess->readDWord (last); - uint32_t nextScreenPtr = g_pProcess->readDWord (last + 4); - while (nextScreenPtr != 0) - { - last = nextScreenPtr; - screenAddr = g_pProcess->readDWord (nextScreenPtr); - nextScreenPtr = g_pProcess->readDWord (nextScreenPtr + 4); - } - return d->offset_descriptor->resolveObjectToClassID (last, screen.type); -} - +*/ +/* bool API::ReadItemTypes(vector< vector< t_itemType > > & itemTypes) { memory_info * minfo = d->offset_descriptor; @@ -1887,4 +815,5 @@ bool API::ReadItemTypes(vector< vector< t_itemType > > & itemTypes) itemTypes.push_back(typesForVec); } return true; -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/dfhack/DFProcess-windows-SHM.cpp b/dfhack/DFProcess-windows-SHM.cpp index 983b1f83e..33a1ceb7b 100644 --- a/dfhack/DFProcess-windows-SHM.cpp +++ b/dfhack/DFProcess-windows-SHM.cpp @@ -22,8 +22,8 @@ must not be misrepresented as being the original software. distribution. */ #include "DFCommonInternal.h" -#include "../shmserver/shms.h" -#include "../shmserver/mod-core.h" +#include "shms.h" +#include "mod-core.h" using namespace DFHack; // a full memory barrier! better be safe than sorry. diff --git a/dfhack/depends/md5/CMakeLists.txt b/dfhack/depends/md5/CMakeLists.txt index 842573605..fc4845bd4 100644 --- a/dfhack/depends/md5/CMakeLists.txt +++ b/dfhack/depends/md5/CMakeLists.txt @@ -1,18 +1 @@ -# main project file. use it from a build sub-folder, see COMPILE for details -PROJECT (dfhack-md5) -cmake_minimum_required(VERSION 2.6) - -# disable warning, autosearch -if(COMMAND cmake_policy) - cmake_policy(SET CMP0003 NEW) -endif(COMMAND cmake_policy) - -if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") - message(SEND_ERROR "In-source builds are not allowed.") -endif("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") - -IF(NOT DEFINED CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") -ENDIF(NOT DEFINED CMAKE_BUILD_TYPE) - ADD_LIBRARY(dfhack-md5 SHARED md5.cpp md5wrapper.cpp) diff --git a/dfhack/depends/tinyxml/CMakeLists.txt b/dfhack/depends/tinyxml/CMakeLists.txt index 888527046..effdf8945 100644 --- a/dfhack/depends/tinyxml/CMakeLists.txt +++ b/dfhack/depends/tinyxml/CMakeLists.txt @@ -1,18 +1 @@ -# main project file. use it from a build sub-folder, see COMPILE for details -PROJECT (dfhack-tixml) -cmake_minimum_required(VERSION 2.6) - -# disable warning, autosearch -if(COMMAND cmake_policy) - cmake_policy(SET CMP0003 NEW) -endif(COMMAND cmake_policy) - -if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") - message(SEND_ERROR "In-source builds are not allowed.") -endif("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") - -IF(NOT DEFINED CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") -ENDIF(NOT DEFINED CMAKE_BUILD_TYPE) - -ADD_LIBRARY(tixml-static SHARED tinystr.cpp tinyxml.cpp tinyxmlerror.cpp tinyxmlparser.cpp) \ No newline at end of file +ADD_LIBRARY(dfhack-tixml SHARED tinystr.cpp tinyxml.cpp tinyxmlerror.cpp tinyxmlparser.cpp) \ No newline at end of file diff --git a/dfhack/include/DFHackAPI.h b/dfhack/include/DFHackAPI.h index 7896cf9b4..70416fa2c 100644 --- a/dfhack/include/DFHackAPI.h +++ b/dfhack/include/DFHackAPI.h @@ -37,15 +37,16 @@ distribution. namespace DFHack { + class APIPrivate; class memory_info; class Process; class DFHACK_EXPORT API { - class Private; - Private * const d; + APIPrivate * const d; public: API(const std::string path_to_xml); ~API(); + /* * Basic control over DF's process state */ @@ -66,236 +67,114 @@ namespace DFHack /// forces resume on Windows. This can be a bad thing with multiple DF tools running! bool ForceResume(); - - /* - * Query the DF's GUI state - */ - ///true if paused, false if not - bool ReadPauseState(); - /// read the DF menu view state (stock screen, unit screen, other screens - bool ReadViewScreen(t_viewscreen &); - /// read the DF menu state (designation menu ect) - uint32_t ReadMenuState(); - - - /* - * Matgloss. next four methods look very similar. I could use two and move the processing one level up... - * I'll keep it like this, even with the code duplication as it will hopefully get more features and separate data types later. - * Yay for nebulous plans for a rock survey tool that tracks how much of which metal could be smelted from available resorces - */ - bool ReadStoneMatgloss(std::vector & output); - bool ReadWoodMatgloss (std::vector & output); - bool ReadMetalMatgloss(std::vector & output); - bool ReadPlantMatgloss(std::vector & output); - bool ReadPlantMatgloss (std::vector & plants); - bool ReadCreatureMatgloss(std::vector & output); - - // read region surroundings, get their vectors of geolayers so we can do translation (or just hand the translation table to the client) - // returns an array of 9 vectors of indices into stone matgloss - /** - Method for reading the geological surrounding of the currently loaded region. - assign is a reference to an array of nine vectors of unsigned words that are to be filled with the data - array is indexed by the BiomeOffset enum - - I omitted resolving the layer matgloss in this API, because it would - introduce overhead by calling some method for each tile. You have to do it - yourself. First get the stuff from ReadGeology and then for each block get - the RegionOffsets. For each tile get the real region from RegionOffsets and - cross-reference it with the geology stuff (region -- array of vectors, depth -- - vector). I'm thinking about turning that Geology stuff into a - two-dimensional array with static size. - - this is the algorithm for applying matgloss: - void DfMap::applyGeoMatgloss(Block * b) - { - // load layer matgloss - for(int x_b = 0; x_b < BLOCK_SIZE; x_b++) - { - for(int y_b = 0; y_b < BLOCK_SIZE; y_b++) - { - int geolayer = b->designation[x_b][y_b].bits.geolayer_index; - int biome = b->designation[x_b][y_b].bits.biome; - b->material[x_b][y_b].type = Mat_Stone; - b->material[x_b][y_b].index = v_geology[b->RegionOffsets[biome]][geolayer]; - } - } - } - */ - bool ReadGeology( std::vector < std::vector >& assign ); - - /* - * BLOCK DATA - */ - /// allocate and read pointers to map blocks - bool InitMap(); - /// destroy the mapblock cache - bool DestroyMap(); - /// get size of the map in tiles - void getSize(uint32_t& x, uint32_t& y, uint32_t& z); - - /** - * Return false/0 on failure, buffer allocated by client app, 256 items long - */ - bool isValidBlock(uint32_t blockx, uint32_t blocky, uint32_t blockz); - /** - * Get the address of a block or 0 if block is not valid - */ - uint32_t getBlockPtr (uint32_t blockx, uint32_t blocky, uint32_t blockz); - - /// read the whole map block at block coords (see DFTypes.h for the block structure) - bool ReadBlock40d(uint32_t blockx, uint32_t blocky, uint32_t blockz, mapblock40d * buffer); - - /// read/write block tile types - bool ReadTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, tiletypes40d *buffer); - bool WriteTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, tiletypes40d *buffer); - /// read/write block designations - bool ReadDesignations(uint32_t blockx, uint32_t blocky, uint32_t blockz, designations40d *buffer); - bool WriteDesignations (uint32_t blockx, uint32_t blocky, uint32_t blockz, designations40d *buffer); - - /// read/write block occupancies - bool ReadOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, occupancies40d *buffer); - bool WriteOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, occupancies40d *buffer); - - /// read/write the block dirty bit - this is used to mark a map block so that DF scans it for designated jobs like digging - bool ReadDirtyBit(uint32_t blockx, uint32_t blocky, uint32_t blockz, bool &dirtybit); - bool WriteDirtyBit(uint32_t blockx, uint32_t blocky, uint32_t blockz, bool dirtybit); - - /// read/write the block flags - bool ReadBlockFlags(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_blockflags &blockflags); - bool WriteBlockFlags(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_blockflags blockflags); + memory_info *getMemoryInfo(); + Process * getProcess(); + DFWindow * getWindow(); - /// read region offsets of a block - used for determining layer stone matgloss - bool ReadRegionOffsets(uint32_t blockx, uint32_t blocky, uint32_t blockz, biome_indices40d *buffer); + /// read/write size bytes of raw data at offset. DANGEROUS, CAN SEGFAULT DF! + void ReadRaw (const uint32_t offset, const uint32_t size, uint8_t *target); + void WriteRaw (const uint32_t offset, const uint32_t size, uint8_t *source); - /// read aggregated veins of a block - bool ReadVeins(uint32_t blockx, uint32_t blocky, uint32_t blockz, std::vector & veins, std::vector & ices); + #include "../modules/Position-proc.h" + #include "../modules/Gui-proc.h" + #include "../modules/Maps-proc.h" + #include "../modules/Materials-proc.h" + #include "../modules/Creatures-proc.h" /* * Constructions (costructed walls, floors, ramps, etc...) */ + /* /// start reading constructions. numconstructions is an output - total constructions present bool InitReadConstructions( uint32_t & numconstructions ); /// read a construiction at index bool ReadConstruction(const int32_t index, t_construction & construction); /// cleanup after reading constructions void FinishReadConstructions(); - +*/ /* * Buildings - also includes zones and stockpiles */ + /* bool InitReadBuildings ( uint32_t & numbuildings ); bool ReadBuilding(const int32_t index, t_building & building); void FinishReadBuildings(); - + */ /* * Effects like mist, dragonfire or dust */ + /* bool InitReadEffects ( uint32_t & numeffects ); bool ReadEffect(const uint32_t index, t_effect_df40d & effect); bool WriteEffect(const uint32_t index, const t_effect_df40d & effect); void FinishReadEffects(); - + */ /* * Trees and shrubs */ + /* bool InitReadVegetation( uint32_t & numplants ); bool ReadVegetation(const int32_t index, t_tree_desc & shrubbery); void FinishReadVegetation(); - - /* - * Creatures - */ - bool InitReadCreatures( uint32_t & numcreatures ); - /** - * Read creatures in a box, starting with index. Returns -1 if no more creatures - * found. Call repeatedly do get all creatures in a specified box (uses tile coords) - */ - int32_t ReadCreatureInBox(const int32_t index, t_creature & furball, - const uint16_t x1, const uint16_t y1,const uint16_t z1, - const uint16_t x2, const uint16_t y2,const uint16_t z2); - bool ReadCreature(const int32_t index, t_creature & furball); - void FinishReadCreatures(); - - /// read/write size bytes of raw data at offset. DANGEROUS, CAN SEGFAULT DF! - void ReadRaw (const uint32_t offset, const uint32_t size, uint8_t *target); - void WriteRaw (const uint32_t offset, const uint32_t size, uint8_t *source); - /// write labors of a creature (for Dwarf Therapist) - bool WriteLabors(const uint32_t index, uint8_t labors[NUM_CREATURE_LABORS]); - + */ + /* * Notes placed by the player */ - + /* /// start reading notes. numnotes is an output - total notes present bool InitReadNotes( uint32_t & numnotes ); /// read note from the note vector at index bool ReadNote(const int32_t index, t_note & note); /// free the note vector void FinishReadNotes(); - + */ /* * Settlements */ + /* bool InitReadSettlements( uint32_t & numsettlements ); bool ReadSettlement(const int32_t index, t_settlement & settlement); bool ReadCurrentSettlement(t_settlement & settlement); void FinishReadSettlements(); - + */ /* * Hotkeys (DF's zoom locations) */ + /* bool InitReadHotkeys( ); bool ReadHotkeys(t_hotkey hotkeys[]); - - /* - * Cursor, and view coords - */ - bool InitViewAndCursor(); - bool getViewCoords (int32_t &x, int32_t &y, int32_t &z); - bool setViewCoords (const int32_t x, const int32_t y, const int32_t z); - - bool getCursorCoords (int32_t &x, int32_t &y, int32_t &z); - bool setCursorCoords (const int32_t x, const int32_t y, const int32_t z); - - /// get the creature vector index of the creature currently under DF' cursor - bool getCurrentCursorCreature (uint32_t & creature_index); - - /* - * Window size in tiles - */ - bool InitViewSize(); - bool getWindowSize(int32_t & width, int32_t & height); - + */ /* * DF translation tables and name translation */ + /* bool InitReadNameTables (std::vector< std::vector > & translations , std::vector< std::vector > & foreign_languages); void FinishReadNameTables(); std::string TranslateName(const t_name & name,const std::vector< std::vector > & translations ,const std::vector< std::vector > & foreign_languages, bool inEnglish=true); - + */ /* * Item reading */ + /* bool InitReadItems(uint32_t & numitems); bool getItemIndexesInBox(std::vector &indexes, const uint16_t x1, const uint16_t y1, const uint16_t z1, const uint16_t x2, const uint16_t y2, const uint16_t z2); bool ReadItem(const uint32_t index, t_item & item); void FinishReadItems(); - + */ /* * Get the other API parts for raw access */ - memory_info *getMemoryInfo(); - Process * getProcess(); - DFWindow * getWindow(); + /* // FIXME: BAD! bool ReadAllMatgloss(vector< vector< string > > & all); */ - bool ReadItemTypes(std::vector< std::vector< t_itemType > > & itemTypes); + //bool ReadItemTypes(std::vector< std::vector< t_itemType > > & itemTypes); }; } // namespace DFHack #endif // SIMPLEAPI_H_INCLUDED diff --git a/dfhack/modules/Creatures.cpp b/dfhack/modules/Creatures.cpp new file mode 100644 index 000000000..b16c7b2ad --- /dev/null +++ b/dfhack/modules/Creatures.cpp @@ -0,0 +1,206 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "DFCommonInternal.h" +#include "../private/APIPrivate.h" + +#define SHMCREATURESHDR ((Creatures::shm_creature_hdr *)d->shm_start) + +using namespace DFHack; +/* +bool API::InitReadCreatures( uint32_t &numcreatures ) +{ + if(!d->InitReadNames()) return false; + try + { + memory_info * minfo = d->offset_descriptor; + Creatures::creature_offsets & off = d->creatures; + off.creature_vector = minfo->getAddress ("creatures"); + off.creature_pos_offset = minfo->getOffset ("creature_position"); + off.creature_type_offset = minfo->getOffset ("creature_race"); + off.creature_flags1_offset = minfo->getOffset ("creature_flags1"); + off.creature_flags2_offset = minfo->getOffset ("creature_flags2"); + off.creature_name_offset = minfo->getOffset ("creature_name"); + off.creature_custom_profession_offset = minfo->getOffset ("creature_custom_profession"); + off.creature_profession_offset = minfo->getOffset ("creature_profession"); + off.creature_sex_offset = minfo->getOffset ("creature_sex"); + off.creature_id_offset = minfo->getOffset ("creature_id"); + off.creature_squad_name_offset = minfo->getOffset ("creature_squad_name"); + off.creature_squad_leader_id_offset = minfo->getOffset ("creature_squad_leader_id"); + off.creature_money_offset = minfo->getOffset ("creature_money"); + off.creature_current_job_offset = minfo->getOffset ("creature_current_job"); + off.creature_current_job_id_offset = minfo->getOffset ("current_job_id"); + off.creature_strength_offset = minfo->getOffset ("creature_strength"); + off.creature_agility_offset = minfo->getOffset ("creature_agility"); + off.creature_toughness_offset = minfo->getOffset ("creature_toughness"); + off.creature_skills_offset = minfo->getOffset ("creature_skills"); + off.creature_labors_offset = minfo->getOffset ("creature_labors"); + off.creature_happiness_offset = minfo->getOffset ("creature_happiness"); + off.creature_traits_offset = minfo->getOffset ("creature_traits"); + off.creature_likes_offset = minfo->getOffset("creature_likes"); + off.creature_artifact_name_offset = minfo->getOffset("creature_artifact_name"); + off.creature_mood_offset = minfo->getOffset("creature_mood"); + + off.creature_pregnancy_offset = minfo->getOffset("creature_pregnancy"); + off.creature_blood_max_offset = minfo->getOffset("creature_blood_max"); + off.creature_blood_current_offset = minfo->getOffset("creature_blood_current"); + off.creature_bleed_offset = minfo->getOffset("creature_bleed"); + + // name offsets for the creature module + off.name_firstname_offset = minfo->getOffset("name_firstname"); + off.name_nickname_offset = minfo->getOffset("name_nickname"); + off.name_words_offset = minfo->getOffset("name_words"); + + // HACK: vector correction. No longer relevant. + off.vector_correct = 0; + + d->p_cre = new DfVector (d->p, off.creature_vector, 4); + d->creaturesInited = true; + numcreatures = d->p_cre->getSize(); + + // --> SHM initialization (if possible) <-- + g_pProcess->getModuleIndex("Creatures40d",1,d->creature_module); + + if(d->creature_module) + { + // supply the module with offsets so it can work with them + memcpy(SHMDATA(Creatures::creature_offsets),&d->creatures,sizeof(Creatures::creature_offsets)); + const uint32_t cmd = Creatures::CREATURE_INIT + (d->creature_module << 16); + g_pProcess->SetAndWait(cmd); + } + return true; + } + catch (Error::MissingMemoryDefinition&) + { + d->creaturesInited = false; + numcreatures = 0; + throw; + } +} + +bool API::ReadCreature (const int32_t index, t_creature & furball) +{ + if(!d->creaturesInited) return false; + if(d->creature_module) + { + // supply the module with offsets so it can work with them + SHMCREATURESHDR->index = index; + const uint32_t cmd = Creatures::CREATURE_AT_INDEX + (d->creature_module << 16); + g_pProcess->SetAndWait(cmd); + memcpy(&furball,SHMDATA(t_creature),sizeof(t_creature)); + // cerr << "creature read from SHM!" << endl; + return true; + } + // read pointer from vector at position + uint32_t temp = * (uint32_t *) d->p_cre->at (index); + furball.origin = temp; + Creatures::creature_offsets &offs = d->creatures; + //read creature from memory + g_pProcess->read (temp + offs.creature_pos_offset, 3 * sizeof (uint16_t), (uint8_t *) & (furball.x)); // xyz really + g_pProcess->readDWord (temp + offs.creature_type_offset, furball.type); + g_pProcess->readDWord (temp + offs.creature_flags1_offset, furball.flags1.whole); + g_pProcess->readDWord (temp + offs.creature_flags2_offset, furball.flags2.whole); + // names + d->readName(furball.name,temp + offs.creature_name_offset); + d->readName(furball.squad_name, temp + offs.creature_squad_name_offset); + d->readName(furball.artifact_name, temp + offs.creature_artifact_name_offset); + // custom profession + fill_char_buf (furball.custom_profession, d->p->readSTLString (temp + offs.creature_custom_profession_offset)); + + // labors + g_pProcess->read (temp + offs.creature_labors_offset, NUM_CREATURE_LABORS, furball.labors); + // traits + g_pProcess->read (temp + offs.creature_traits_offset, sizeof (uint16_t) * NUM_CREATURE_TRAITS, (uint8_t *) &furball.traits); + // learned skills + DfVector skills (d->p, temp + offs.creature_skills_offset, 4 ); + furball.numSkills = skills.getSize(); + for (uint32_t i = 0; i < furball.numSkills;i++) + { + uint32_t temp2 = * (uint32_t *) skills[i]; + //skills.read(i, (uint8_t *) &temp2); + // a byte: this gives us 256 skills maximum. + furball.skills[i].id = g_pProcess->readByte (temp2); + furball.skills[i].rating = g_pProcess->readByte (temp2 + 4); + furball.skills[i].experience = g_pProcess->readWord (temp2 + 8); + } + // profession + furball.profession = g_pProcess->readByte (temp + offs.creature_profession_offset); + // current job HACK: the job object isn't cleanly represented here + uint32_t jobIdAddr = g_pProcess->readDWord (temp + offs.creature_current_job_offset); + + if (jobIdAddr) + { + furball.current_job.active = true; + furball.current_job.jobId = g_pProcess->readByte (jobIdAddr + offs.creature_current_job_id_offset); + } + else + { + furball.current_job.active = false; + } + + //likes + DfVector likes(d->p, temp + offs.creature_likes_offset, 4); + furball.numLikes = likes.getSize(); + for(uint32_t i = 0;iread(temp2,sizeof(t_like),(uint8_t *) &furball.likes[i]); + } + + furball.mood = (int16_t) g_pProcess->readWord (temp + offs.creature_mood_offset); + + + g_pProcess->readDWord (temp + offs.creature_happiness_offset, furball.happiness); + g_pProcess->readDWord (temp + offs.creature_id_offset, furball.id); + g_pProcess->readDWord (temp + offs.creature_agility_offset, furball.agility); + g_pProcess->readDWord (temp + offs.creature_strength_offset, furball.strength); + g_pProcess->readDWord (temp + offs.creature_toughness_offset, furball.toughness); + g_pProcess->readDWord (temp + offs.creature_money_offset, furball.money); + furball.squad_leader_id = (int32_t) g_pProcess->readDWord (temp + offs.creature_squad_leader_id_offset); + g_pProcess->readByte (temp + offs.creature_sex_offset, furball.sex); + + g_pProcess->readDWord(temp + offs.creature_pregnancy_offset, furball.pregnancy_timer); + furball.blood_max = (int32_t) g_pProcess->readDWord(temp + offs.creature_blood_max_offset); + furball.blood_current = (int32_t) g_pProcess->readDWord(temp + offs.creature_blood_current_offset); + g_pProcess->readDWord(temp + offs.creature_bleed_offset, furball.bleed_rate); + + + return true; +} + +bool API::WriteLabors(const uint32_t index, uint8_t labors[NUM_CREATURE_LABORS]) +{ + if(!d->creaturesInited) return false; + uint32_t temp = * (uint32_t *) d->p_cre->at (index); + WriteRaw(temp + d->creatures.creature_labors_offset, NUM_CREATURE_LABORS, labors); +} + + +bool API::getCurrentCursorCreature(uint32_t & creature_index) +{ + if(!d->cursorWindowInited) return false; + creature_index = g_pProcess->readDWord(d->current_cursor_creature_offset); + return true; +} +*/ \ No newline at end of file diff --git a/dfhack/modules/Gui.cpp b/dfhack/modules/Gui.cpp new file mode 100644 index 000000000..d7b814e6e --- /dev/null +++ b/dfhack/modules/Gui.cpp @@ -0,0 +1,59 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "DFCommonInternal.h" +#include "../private/APIPrivate.h" +using namespace DFHack; + +bool API::ReadPauseState() +{ + // replace with an exception + if(!d->cursorWindowInited) return false; + + uint32_t pauseState = g_pProcess->readDWord (d->pause_state_offset); + return pauseState & 1; +} + +uint32_t API::ReadMenuState() +{ + if(d->cursorWindowInited) + return(g_pProcess->readDWord(d->current_menu_state_offset)); + return false; +} + +bool API::ReadViewScreen (t_viewscreen &screen) +{ + if (!d->cursorWindowInited) return false; + + uint32_t last = g_pProcess->readDWord (d->view_screen_offset); + uint32_t screenAddr = g_pProcess->readDWord (last); + uint32_t nextScreenPtr = g_pProcess->readDWord (last + 4); + while (nextScreenPtr != 0) + { + last = nextScreenPtr; + screenAddr = g_pProcess->readDWord (nextScreenPtr); + nextScreenPtr = g_pProcess->readDWord (nextScreenPtr + 4); + } + return d->offset_descriptor->resolveObjectToClassID (last, screen.type); +} diff --git a/dfhack/modules/Maps.cpp b/dfhack/modules/Maps.cpp new file mode 100644 index 000000000..75008851e --- /dev/null +++ b/dfhack/modules/Maps.cpp @@ -0,0 +1,442 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "DFCommonInternal.h" +#include "../private/APIPrivate.h" + +#define SHMMAPSHDR ((Maps::shm_maps_hdr *)d->shm_start) + +using namespace DFHack; + +/*-----------------------------------* + * Init the mapblock pointer array * + *-----------------------------------*/ +bool API::InitMap() +{ + uint32_t map_offset = d->offset_descriptor->getAddress ("map_data"); + uint32_t x_count_offset = d->offset_descriptor->getAddress ("x_count_block"); + uint32_t y_count_offset = d->offset_descriptor->getAddress ("y_count_block"); + uint32_t z_count_offset = d->offset_descriptor->getAddress ("z_count_block"); + + // get the offsets once here + d->tile_type_offset = d->offset_descriptor->getOffset ("type"); + d->designation_offset = d->offset_descriptor->getOffset ("designation"); + //d->occupancy_offset = d->offset_descriptor->getOffset ("occupancy"); + //d->biome_stuffs = d->offset_descriptor->getOffset ("biome_stuffs"); + + d->veinvector = d->offset_descriptor->getOffset ("v_vein"); + + // these can fail and will be found when looking at the actual veins later + // basically a cache + d->vein_ice_vptr = 0; + d->offset_descriptor->resolveClassnameToVPtr("block_square_event_frozen_liquid", d->vein_ice_vptr); + d->vein_mineral_vptr = 0; + d->offset_descriptor->resolveClassnameToVPtr("block_square_event_mineral",d->vein_mineral_vptr); + + /* + * --> SHM initialization (if possible) <-- + */ + g_pProcess->getModuleIndex("Maps2010",1,d->maps_module); + + if(d->maps_module) + { + // supply the module with offsets so it can work with them + Maps::maps_offsets *off = SHMDATA(Maps::maps_offsets); + off->designation_offset = d->designation_offset; + off->map_offset = map_offset; + off->tile_type_offset = d->tile_type_offset; + off->vein_ice_vptr = d->vein_ice_vptr; // FIXME: not necessarily true, the shm server side needs a class lookup >_< + off->vein_mineral_vptr = d->vein_mineral_vptr; // FIXME: not necessarily true, the shm server side needs a class lookup >_< + off->veinvector = d->veinvector; + off->x_count_offset = x_count_offset; + off->y_count_offset = y_count_offset; + off->z_count_offset = z_count_offset; + full_barrier + const uint32_t cmd = Maps::MAP_INIT + (d->maps_module << 16); + g_pProcess->SetAndWait(cmd); + //cerr << "Map acceleration enabled!" << endl; + } + + // get the map pointer + uint32_t x_array_loc = g_pProcess->readDWord (map_offset); + if (!x_array_loc) + { + return false; + // FIXME: only throw this due to programmer error, in the other map functions + //throw Error::NoMapLoaded(); + } + + // get the size + uint32_t mx, my, mz; + mx = d->x_block_count = g_pProcess->readDWord (x_count_offset); + my = d->y_block_count = g_pProcess->readDWord (y_count_offset); + mz = d->z_block_count = g_pProcess->readDWord (z_count_offset); + + // test for wrong map dimensions + if (mx == 0 || mx > 48 || my == 0 || my > 48 || mz == 0) + { + throw Error::BadMapDimensions(mx, my); + //return false; + } + + // alloc array for pointers to all blocks + d->block = new uint32_t[mx*my*mz]; + uint32_t *temp_x = new uint32_t[mx]; + uint32_t *temp_y = new uint32_t[my]; + uint32_t *temp_z = new uint32_t[mz]; + + g_pProcess->read (x_array_loc, mx * sizeof (uint32_t), (uint8_t *) temp_x); + for (uint32_t x = 0; x < mx; x++) + { + g_pProcess->read (temp_x[x], my * sizeof (uint32_t), (uint8_t *) temp_y); + // y -> map column + for (uint32_t y = 0; y < my; y++) + { + g_pProcess->read (temp_y[y], + mz * sizeof (uint32_t), + (uint8_t *) (d->block + x*my*mz + y*mz)); + } + } + delete [] temp_x; + delete [] temp_y; + delete [] temp_z; + return true; +} + +bool API::DestroyMap() +{ + if (d->block != NULL) + { + delete [] d->block; + d->block = NULL; + } + return true; +} + +bool API::isValidBlock (uint32_t x, uint32_t y, uint32_t z) +{ + if ( x >= d->x_block_count || y >= d->y_block_count || z >= d->z_block_count) + return false; + return d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z] != 0; +} + +uint32_t API::getBlockPtr (uint32_t x, uint32_t y, uint32_t z) +{ + if ( x >= d->x_block_count || y >= d->y_block_count || z >= d->z_block_count) + return 0; + return d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; +} + +bool API::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer) +{ + if(d->shm_start && d->maps_module) // ACCELERATE! + { + SHMMAPSHDR->x = x; + SHMMAPSHDR->y = y; + SHMMAPSHDR->z = z; + volatile uint32_t cmd = Maps::MAP_READ_BLOCK_BY_COORDS + (d->maps_module << 16); + if(!g_pProcess->SetAndWait(cmd)) + return false; + memcpy(buffer,SHMDATA(mapblock40d),sizeof(mapblock40d)); + return true; + } + else // plain old block read + { + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + g_pProcess->read (addr + d->tile_type_offset, sizeof (buffer->tiletypes), (uint8_t *) buffer->tiletypes); + buffer->origin = addr; + uint32_t addr_of_struct = g_pProcess->readDWord(addr); + buffer->blockflags.whole = g_pProcess->readDWord(addr_of_struct); + return true; + } + return false; + } +} + + +// 256 * sizeof(uint16_t) +bool API::ReadTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + g_pProcess->read (addr + d->tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); + return true; + } + return false; +} + +bool API::ReadDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool &dirtybit) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if(addr) + { + uint32_t addr_of_struct = g_pProcess->readDWord(addr); + dirtybit = g_pProcess->readDWord(addr_of_struct) & 1; + return true; + } + return false; +} + +bool API::WriteDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool dirtybit) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + uint32_t addr_of_struct = g_pProcess->readDWord(addr); + uint32_t dirtydword = g_pProcess->readDWord(addr_of_struct); + dirtydword &= 0xFFFFFFFE; + dirtydword |= (uint32_t) dirtybit; + g_pProcess->writeDWord (addr_of_struct, dirtydword); + return true; + } + return false; +} + +/// read/write the block flags +bool API::ReadBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags &blockflags) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if(addr) + { + uint32_t addr_of_struct = g_pProcess->readDWord(addr); + blockflags.whole = g_pProcess->readDWord(addr_of_struct); + return true; + } + return false; +} +bool API::WriteBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags blockflags) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + uint32_t addr_of_struct = g_pProcess->readDWord(addr); + g_pProcess->writeDWord (addr_of_struct, blockflags.whole); + return true; + } + return false; +} + +bool API::ReadDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + g_pProcess->read (addr + d->designation_offset, sizeof (designations40d), (uint8_t *) buffer); + return true; + } + return false; +} + +// 256 * sizeof(uint16_t) +bool API::WriteTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + g_pProcess->write (addr + d->tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); + return true; + } + return false; +} + + +// 256 * sizeof(uint32_t) +bool API::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + g_pProcess->write (addr + d->designation_offset, sizeof (designations40d), (uint8_t *) buffer); + return true; + } + return false; +} + +// FIXME: this is bad. determine the real size! +//16 of them? IDK... there's probably just 7. Reading more doesn't cause errors as it's an array nested inside a block +// 16 * sizeof(uint8_t) +/* +bool API::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40d *buffer) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + if (addr) + { + g_pProcess->read (addr + d->biome_stuffs, sizeof (biome_indices40d), (uint8_t *) buffer); + return true; + } + return false; +} +*/ + +// veins of a block, expects empty vein vectors +bool API::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector & veins, vector & ices) +{ + uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; + veins.clear(); + ices.clear(); + if (addr && d->veinvector) + { + // veins are stored as a vector of pointers to veins + /*pointer is 4 bytes! we work with a 32bit program here, no matter what architecture we compile khazad for*/ + DfVector p_veins (d->p, addr + d->veinvector, 4); + uint32_t size = p_veins.getSize(); + veins.reserve (size); + + // read all veins + for (uint32_t i = 0; i < size;i++) + { + t_vein v; + t_frozenliquidvein fv; + + // read the vein pointer from the vector + uint32_t temp = * (uint32_t *) p_veins[i]; + uint32_t type = g_pProcess->readDWord(temp); +try_again: + if(type == d->vein_mineral_vptr) + { + // read the vein data (dereference pointer) + g_pProcess->read (temp, sizeof(t_vein), (uint8_t *) &v); + v.address_of = temp; + // store it in the vector + veins.push_back (v); + } + else if(type == d->vein_ice_vptr) + { + // read the ice vein data (dereference pointer) + g_pProcess->read (temp, sizeof(t_frozenliquidvein), (uint8_t *) &fv); + // store it in the vector + ices.push_back (fv); + } + else if(g_pProcess->readClassName(type) == "block_square_event_frozen_liquid") + { + d->vein_ice_vptr = type; + goto try_again; + } + else if(g_pProcess->readClassName(type) == "block_square_event_mineral") + { + d->vein_mineral_vptr = type; + goto try_again; + } + } + return true; + } + return false; +} + + +// getter for map size +void API::getSize (uint32_t& x, uint32_t& y, uint32_t& z) +{ + x = d->x_block_count; + y = d->y_block_count; + z = d->z_block_count; +} + +/* +//vector v_geology[eBiomeCount]; +bool API::ReadGeology (vector < vector >& assign) +{ + memory_info * minfo = d->offset_descriptor; + // get needed addresses and offsets. Now this is what I call crazy. + int region_x_offset = minfo->getAddress ("region_x"); + int region_y_offset = minfo->getAddress ("region_y"); + int region_z_offset = minfo->getAddress ("region_z"); + int world_offset = minfo->getAddress ("world"); + int world_regions_offset = minfo->getOffset ("w_regions_arr"); + int region_size = minfo->getHexValue ("region_size"); + int region_geo_index_offset = minfo->getOffset ("region_geo_index_off"); + int world_geoblocks_offset = minfo->getOffset ("w_geoblocks"); + int world_size_x = minfo->getOffset ("world_size_x"); + int world_size_y = minfo->getOffset ("world_size_y"); + int geolayer_geoblock_offset = minfo->getOffset ("geolayer_geoblock_offset"); + + uint32_t regionX, regionY, regionZ; + uint16_t worldSizeX, worldSizeY; + + // read position of the region inside DF world + g_pProcess->readDWord (region_x_offset, regionX); + g_pProcess->readDWord (region_y_offset, regionY); + g_pProcess->readDWord (region_z_offset, regionZ); + + // get world size + g_pProcess->readWord (world_offset + world_size_x, worldSizeX); + g_pProcess->readWord (world_offset + world_size_y, worldSizeY); + + // get pointer to first part of 2d array of regions + uint32_t regions = g_pProcess->readDWord (world_offset + world_regions_offset); + + // read the geoblock vector + DfVector geoblocks (d->p, world_offset + world_geoblocks_offset, 4); + + // iterate over 8 surrounding regions + local region + for (int i = eNorthWest; i < eBiomeCount; i++) + { + // check bounds, fix them if needed + int bioRX = regionX / 16 + (i % 3) - 1; + if (bioRX < 0) bioRX = 0; + if (bioRX >= worldSizeX) bioRX = worldSizeX - 1; + int bioRY = regionY / 16 + (i / 3) - 1; + if (bioRY < 0) bioRY = 0; + if (bioRY >= worldSizeY) bioRY = worldSizeY - 1; + + // get pointer to column of regions + uint32_t geoX; + g_pProcess->readDWord (regions + bioRX*4, geoX); + + // get index into geoblock vector + uint16_t geoindex; + g_pProcess->readWord (geoX + bioRY*region_size + region_geo_index_offset, geoindex); + + // get the geoblock from the geoblock vector using the geoindex + // read the matgloss pointer from the vector into temp + uint32_t geoblock_off = * (uint32_t *) geoblocks[geoindex]; + + // get the vector with pointer to layers + DfVector geolayers (d->p, geoblock_off + geolayer_geoblock_offset , 4); // let's hope + // make sure we don't load crap + assert (geolayers.getSize() > 0 && geolayers.getSize() <= 16); + + d->v_geology[i].reserve (geolayers.getSize()); + // finally, read the layer matgloss + for (uint32_t j = 0;j < geolayers.getSize();j++) + { + // read pointer to a layer + uint32_t geol_offset = * (uint32_t *) geolayers[j]; + // read word at pointer + 2, store in our geology vectors + d->v_geology[i].push_back (g_pProcess->readWord (geol_offset + 2)); + } + } + assign.clear(); + assign.reserve (eBiomeCount); +// // TODO: clean this up + for (int i = 0; i < eBiomeCount;i++) + { + assign.push_back (d->v_geology[i]); + } + return true; +} +*/ \ No newline at end of file diff --git a/dfhack/modules/Materials.cpp b/dfhack/modules/Materials.cpp new file mode 100644 index 000000000..756a95bf8 --- /dev/null +++ b/dfhack/modules/Materials.cpp @@ -0,0 +1,188 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "DFCommonInternal.h" +#include "../private/APIPrivate.h" +using namespace DFHack; + + + +bool API::ReadWoodMaterials (vector & woods) +{ +/* + int matgloss_address = d->offset_descriptor->getAddress ("matgloss"); + int matgloss_wood_name_offset = d->offset_descriptor->getOffset("matgloss_wood_name"); + // TODO: find flag for autumnal coloring? + DfVector p_matgloss(d->p, matgloss_address, 4); + + woods.clear(); + + t_matgloss mat; + // TODO: use brown? + mat.fore = 7; + mat.back = 0; + mat.bright = 0; + uint32_t size = p_matgloss.getSize(); + for (uint32_t i = 0; i < size ;i++) + { + // read the matgloss pointer from the vector into temp + uint32_t temp = * (uint32_t *) p_matgloss[i]; + // read the string pointed at by + d->p->readSTLString (temp, mat.id, 128); + d->p->readSTLString (temp+matgloss_wood_name_offset, mat.name, 128); + woods.push_back (mat); + } + */ + return true; +} + +bool API::ReadInorganicMaterials (vector & inorganic) +{ + memory_info * minfo = d->offset_descriptor; + int matgloss_address = minfo->getAddress ("mat_inorganics"); + //int matgloss_colors = minfo->getOffset ("material_color"); + //int matgloss_stone_name_offset = minfo->getOffset("matgloss_stone_name"); + + DfVector p_matgloss (d->p, matgloss_address, 4); + + uint32_t size = p_matgloss.getSize(); + inorganic.resize (0); + inorganic.reserve (size); + for (uint32_t i = 0; i < size;i++) + { + // read the matgloss pointer from the vector into temp + uint32_t temp = * (uint32_t *) p_matgloss[i]; + // read the string pointed at by + t_matgloss mat; + //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address + d->p->readSTLString (temp, mat.id, 128); + /* + d->p->readSTLString (temp+matgloss_stone_name_offset, mat.name, 128); + mat.fore = (uint8_t) g_pProcess->readWord (temp + matgloss_colors); + mat.back = (uint8_t) g_pProcess->readWord (temp + matgloss_colors + 2); + mat.bright = (uint8_t) g_pProcess->readWord (temp + matgloss_colors + 4); + */ + inorganic.push_back (mat); + } + return true; + +} + +bool API::ReadPlantMaterials (vector & plants) +{ + /* + memory_info * minfo = d->offset_descriptor; + int matgloss_address = minfo->getAddress ("matgloss"); + int matgloss_offset = minfo->getHexValue ("matgloss_skip"); + int matgloss_plant_name_offset = minfo->getOffset("matgloss_plant_name"); + DfVector p_matgloss(d->p, matgloss_address + matgloss_offset * 2, 4); + + plants.clear(); + + // TODO: use green? + t_matgloss mat; + mat.fore = 7; + mat.back = 0; + mat.bright = 0; + for (uint32_t i = 0; i < p_matgloss.getSize();i++) + { + // read the matgloss pointer from the vector into temp + uint32_t temp = * (uint32_t *) p_matgloss[i]; + // read the string pointed at by + //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address + d->p->readSTLString (temp, mat.id, 128); + d->p->readSTLString (temp+matgloss_plant_name_offset, mat.name, 128); + plants.push_back (mat); + } + */ + return true; +} + +bool API::ReadPlantMaterials (vector & plants) +{ + /* + memory_info * minfo = d->offset_descriptor; + int matgloss_address = minfo->getAddress ("matgloss"); + int matgloss_offset = minfo->getHexValue ("matgloss_skip"); + int matgloss_plant_name_offset = minfo->getOffset("matgloss_plant_name"); + int matgloss_plant_drink_offset = minfo->getOffset("matgloss_plant_drink"); + int matgloss_plant_food_offset = minfo->getOffset("matgloss_plant_food"); + int matgloss_plant_extract_offset = minfo->getOffset("matgloss_plant_extract"); + DfVector p_matgloss(d->p, matgloss_address + matgloss_offset * 2, 4); + + plants.clear(); + + // TODO: use green? + t_matglossPlant mat; + mat.fore = 7; + mat.back = 0; + mat.bright = 0; + for (uint32_t i = 0; i < p_matgloss.getSize();i++) + { + // read the matgloss pointer from the vector into temp + uint32_t temp = * (uint32_t *) p_matgloss[i]; + // read the string pointed at by + //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address + d->p->readSTLString (temp, mat.id, 128); + d->p->readSTLString (temp+matgloss_plant_name_offset, mat.name, 128); + d->p->readSTLString (temp+matgloss_plant_drink_offset, mat.drink_name, 128); + d->p->readSTLString (temp+matgloss_plant_food_offset, mat.food_name, 128); + d->p->readSTLString (temp+matgloss_plant_extract_offset, mat.extract_name, 128); + + //d->p->readSTLString (temp + plants.push_back (mat); + } + */ + return true; +} + +bool API::ReadCreatureTypes (vector & creatures) +{ + /* + memory_info * minfo = d->offset_descriptor; + int matgloss_address = minfo->getAddress ("matgloss"); + int matgloss_offset = minfo->getHexValue ("matgloss_skip"); + int matgloss_creature_name_offset = minfo->getOffset("matgloss_creature_name"); + DfVector p_matgloss (d->p, matgloss_address + matgloss_offset * 6, 4); + + creatures.clear(); + + // TODO: use green? + t_matgloss mat; + mat.fore = 7; + mat.back = 0; + mat.bright = 0; + for (uint32_t i = 0; i < p_matgloss.getSize();i++) + { + // read the matgloss pointer from the vector into temp + uint32_t temp = * (uint32_t *) p_matgloss[i]; + // read the string pointed at by + //fill_char_buf(mat.id, d->p->readSTLString(temp)); // reads a C string given an address + d->p->readSTLString (temp, mat.id, 128); + d->p->readSTLString (temp+matgloss_creature_name_offset, mat.name, 128); + creatures.push_back (mat); + } + */ + return true; +} diff --git a/dfhack/modules/Position.cpp b/dfhack/modules/Position.cpp new file mode 100644 index 000000000..679d1b10c --- /dev/null +++ b/dfhack/modules/Position.cpp @@ -0,0 +1,103 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "DFCommonInternal.h" +#include "../private/APIPrivate.h" +using namespace DFHack; + +bool API::InitViewAndCursor() +{ + try + { + d->window_x_offset = d->offset_descriptor->getAddress ("window_x"); + d->window_y_offset = d->offset_descriptor->getAddress ("window_y"); + d->window_z_offset = d->offset_descriptor->getAddress ("window_z"); + d->cursor_xyz_offset = d->offset_descriptor->getAddress ("cursor_xyz"); + d->current_cursor_creature_offset = d->offset_descriptor->getAddress ("current_cursor_creature"); + d->window_dims_offset = d->offset_descriptor->getAddress ("window_dims"); + + d->current_menu_state_offset = d->offset_descriptor->getAddress("current_menu_state"); + d->pause_state_offset = d->offset_descriptor->getAddress ("pause_state"); + d->view_screen_offset = d->offset_descriptor->getAddress ("view_screen"); + + d->cursorWindowInited = true; + return true; + } + catch (Error::MissingMemoryDefinition&) + { + d->cursorWindowInited = false; + throw; + } +} + +bool API::getViewCoords (int32_t &x, int32_t &y, int32_t &z) +{ + if (!d->cursorWindowInited) return false; + g_pProcess->readDWord (d->window_x_offset, (uint32_t &) x); + g_pProcess->readDWord (d->window_y_offset, (uint32_t &) y); + g_pProcess->readDWord (d->window_z_offset, (uint32_t &) z); + return true; +} + +//FIXME: confine writing of coords to map bounds? +bool API::setViewCoords (const int32_t x, const int32_t y, const int32_t z) +{ + if (!d->cursorWindowInited) return false; + g_pProcess->writeDWord (d->window_x_offset, (uint32_t) x); + g_pProcess->writeDWord (d->window_y_offset, (uint32_t) y); + g_pProcess->writeDWord (d->window_z_offset, (uint32_t) z); + return true; +} + +bool API::getCursorCoords (int32_t &x, int32_t &y, int32_t &z) +{ + if(!d->cursorWindowInited) return false; + int32_t coords[3]; + g_pProcess->read (d->cursor_xyz_offset, 3*sizeof (int32_t), (uint8_t *) coords); + x = coords[0]; + y = coords[1]; + z = coords[2]; + if (x == -30000) return false; + return true; +} + +//FIXME: confine writing of coords to map bounds? +bool API::setCursorCoords (const int32_t x, const int32_t y, const int32_t z) +{ + if (!d->cursorWindowInited) return false; + int32_t coords[3] = {x, y, z}; + g_pProcess->write (d->cursor_xyz_offset, 3*sizeof (int32_t), (uint8_t *) coords); + return true; +} + +bool API::getWindowSize (int32_t &width, int32_t &height) +{ + if(! d->cursorWindowInited) return false; + + int32_t coords[2]; + g_pProcess->read (d->window_dims_offset, 2*sizeof (int32_t), (uint8_t *) coords); + width = coords[0]; + height = coords[1]; + return true; +} \ No newline at end of file diff --git a/dfhack/private/APIPrivate.h b/dfhack/private/APIPrivate.h new file mode 100644 index 000000000..6c3752a7f --- /dev/null +++ b/dfhack/private/APIPrivate.h @@ -0,0 +1,113 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* +* WARNING: Only include from API modules +*/ + +#ifndef APIPRIVATE_H_INCLUDED +#define APIPRIVATE_H_INCLUDED + +// we connect to those +#include +#include +#include +#include + +#define SHMCMD(num) ((shm_cmd *)d->shm_start)[num]->pingpong +#define SHMHDR ((shm_core_hdr *)d->shm_start) +#define SHMDATA(type) ((type *)(d->shm_start + SHM_HEADER)) + +namespace DFHack +{ + +class APIPrivate +{ +public: + APIPrivate(); + void readName(t_name & name, uint32_t address); + // get the name offsets + bool InitReadNames(); + + #include "../modules/Creatures-data.h" + #include "../modules/Maps-data.h" + #include "../modules/Position-data.h" + #include "../modules/Gui-data.h" + #include "../modules/Materials-data.h" + + uint32_t name_firstname_offset; + uint32_t name_nickname_offset; + uint32_t name_words_offset; + + ProcessEnumerator* pm; + Process* p; + char * shm_start; + memory_info* offset_descriptor; + string xml; + + /* + uint32_t item_material_offset; + + uint32_t note_foreground_offset; + uint32_t note_background_offset; + uint32_t note_name_offset; + uint32_t note_xyz_offset; + uint32_t hotkey_start; + uint32_t hotkey_mode_offset; + uint32_t hotkey_xyz_offset; + uint32_t hotkey_size; + + uint32_t settlement_name_offset; + uint32_t settlement_world_xy_offset; + uint32_t settlement_local_xy_offset; + + uint32_t dwarf_lang_table_offset; + + bool constructionsInited; + bool buildingsInited; + bool effectsInited; + bool vegetationInited; + + + bool itemsInited; + bool notesInited; + bool namesInited; + bool hotkeyInited; + bool settlementsInited; + bool nameTablesInited; + + uint32_t tree_offset; + + DfVector *p_cons; + DfVector *p_bld; + DfVector *p_effect; + DfVector *p_veg; + DfVector *p_itm; + DfVector *p_notes; + DfVector *p_settlements; + DfVector *p_current_settlement; + */ +}; +} +#endif \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2e3be5a4b..1181c83d6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -9,17 +9,17 @@ ENDIF(UNIX) ADD_EXECUTABLE(dfattachtest attachtest.cpp) TARGET_LINK_LIBRARIES(dfattachtest dfhack) +# buildingsdump - dump buildings and their raw data filtered by type +#ADD_EXECUTABLE(dfbuildingsdump buildingsdump.cpp) +#TARGET_LINK_LIBRARIES(dfbuildingsdump dfhack) + # a benchmark program, reads the map 1000x ADD_EXECUTABLE(dfexpbench expbench.cpp) TARGET_LINK_LIBRARIES(dfexpbench dfhack) # creaturedump - basic creature dump - a test of the creature related exports -ADD_EXECUTABLE(dfcreaturedump creaturedump.cpp) -TARGET_LINK_LIBRARIES(dfcreaturedump dfhack) - -# buildingsdump - dump buildings and their raw data filtered by type -ADD_EXECUTABLE(dfbuildingsdump buildingsdump.cpp) -TARGET_LINK_LIBRARIES(dfbuildingsdump dfhack) +# ADD_EXECUTABLE(dfcreaturedump creaturedump.cpp) +# TARGET_LINK_LIBRARIES(dfcreaturedump dfhack) # materialtest - just list the first material of each type ADD_EXECUTABLE(dfmaterialtest materialtest.cpp) @@ -35,13 +35,13 @@ ADD_EXECUTABLE(dfsuspend suspendtest.cpp) TARGET_LINK_LIBRARIES(dfsuspend dfhack) # itemdump - dump the item under the cursor -ADD_EXECUTABLE(dfitemdump dfitemdump.cpp) -TARGET_LINK_LIBRARIES(dfitemdump dfhack) +# ADD_EXECUTABLE(dfitemdump dfitemdump.cpp) +# TARGET_LINK_LIBRARIES(dfitemdump dfhack) # hotkeynotedump - dumps the hotkeys and notes for the loaded map # Author: belal -ADD_EXECUTABLE(dfhotkeynotedump hotkeynotedump.cpp) -TARGET_LINK_LIBRARIES(dfhotkeynotedump dfhack) +# ADD_EXECUTABLE(dfhotkeynotedump hotkeynotedump.cpp) +# TARGET_LINK_LIBRARIES(dfhotkeynotedump dfhack) # findnameindexes # Author: belal @@ -50,8 +50,8 @@ TARGET_LINK_LIBRARIES(dfhotkeynotedump dfhack) # settlementdump - dumps the settlements on the loaded map # Author: belal -ADD_EXECUTABLE(dfsettlementdump settlementdump.cpp) -TARGET_LINK_LIBRARIES(dfsettlementdump dfhack) +# ADD_EXECUTABLE(dfsettlementdump settlementdump.cpp) +# TARGET_LINK_LIBRARIES(dfsettlementdump dfhack) # veccheck - read vector values at address ADD_EXECUTABLE(dfvecc veccheck.cpp) @@ -59,67 +59,67 @@ TARGET_LINK_LIBRARIES(dfvecc dfhack) # catsplosion - Makes every cat pregnant, and almost due... # Author: Zhentar -ADD_EXECUTABLE(dfcatsplosion catsplosion.cpp) -TARGET_LINK_LIBRARIES(dfcatsplosion dfhack) - -IF(UNIX) - SET(CURSES_NEED_WIDE "YES") - SET(CURSES_NEED_NCURSES "YES") - find_package(Curses) - - IF(CURSES_FOUND) - if(CURSES_HAVE_NCURSESW_NCURSES_H) - SET(NCURSES_H "ncursesw/ncurses.h") - elseif(CURSES_HAVE_NCURSESW_CURSES_H) - SET(NCURSES_H "ncursesw/curses.h") - elseif(CURSES_HAVE_NCURSESW_H) - SET(NCURSES_H "ncursesw.h") - elseif(CURSES_HAVE_CURSESW_H) - SET(NCURSES_H "cursesw.h") - endif(CURSES_HAVE_NCURSESW_NCURSES_H) - IF(NCURSES_H) - # OPTION( VARIABLE "Description" Initial state) - #OPTION( WITH_FOO "Enable FOO support" ON ) - #OPTION( WITH_BAR "Enable BAR component" OFF ) - #SET( BAZ 18 ) - CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/fake-curses.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/fake-curses.h ) - - # veinlook - look at the map... sort of - ADD_EXECUTABLE(dfveinlook veinlook.cpp) - INCLUDE_DIRECTORIES(${CURSES_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(dfveinlook dfhack ${CURSES_LIBRARIES}) - install(TARGETS - dfveinlook - RUNTIME DESTINATION bin - ) - ENDIF(NCURSES_H) - ELSE(CURSES_FOUND) - MESSAGE(STATUS "Wide-character ncurses library not found - vainlook can't be built") - ENDIF(CURSES_FOUND) -ENDIF(UNIX) +# ADD_EXECUTABLE(dfcatsplosion catsplosion.cpp) +# TARGET_LINK_LIBRARIES(dfcatsplosion dfhack) + +# IF(UNIX) +# SET(CURSES_NEED_WIDE "YES") +# SET(CURSES_NEED_NCURSES "YES") +# find_package(Curses) +# +# IF(CURSES_FOUND) +# if(CURSES_HAVE_NCURSESW_NCURSES_H) +# SET(NCURSES_H "ncursesw/ncurses.h") +# elseif(CURSES_HAVE_NCURSESW_CURSES_H) +# SET(NCURSES_H "ncursesw/curses.h") +# elseif(CURSES_HAVE_NCURSESW_H) +# SET(NCURSES_H "ncursesw.h") +# elseif(CURSES_HAVE_CURSESW_H) +# SET(NCURSES_H "cursesw.h") +# endif(CURSES_HAVE_NCURSESW_NCURSES_H) +# IF(NCURSES_H) +# # OPTION( VARIABLE "Description" Initial state) +# #OPTION( WITH_FOO "Enable FOO support" ON ) +# #OPTION( WITH_BAR "Enable BAR component" OFF ) +# #SET( BAZ 18 ) +# CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/fake-curses.h.cmake ${CMAKE_CURRENT_SOURCE_DIR}/fake-curses.h ) +# +# # veinlook - look at the map... sort of +# ADD_EXECUTABLE(dfveinlook veinlook.cpp) +# INCLUDE_DIRECTORIES(${CURSES_INCLUDE_DIR}) +# TARGET_LINK_LIBRARIES(dfveinlook dfhack ${CURSES_LIBRARIES}) +# install(TARGETS +# dfveinlook +# RUNTIME DESTINATION bin +# ) +# ENDIF(NCURSES_H) +# ELSE(CURSES_FOUND) +# MESSAGE(STATUS "Wide-character ncurses library not found - vainlook can't be built") +# ENDIF(CURSES_FOUND) +# ENDIF(UNIX) # renamer - change the custom names and professions of creatures, sends keys to # df directly # Author: belal -ADD_EXECUTABLE(dfrenamer renamer.cpp) -TARGET_LINK_LIBRARIES(dfrenamer dfhack) +#ADD_EXECUTABLE(dfrenamer renamer.cpp) +#TARGET_LINK_LIBRARIES(dfrenamer dfhack) IF(UNIX) install(TARGETS dfattachtest -dfexpbench -dfcreaturedump -dfbuildingsdump +#dfexpbench +#dfcreaturedump +#dfbuildingsdump dfmaterialtest dfposition dfsuspend -dfitemdump -dfhotkeynotedump +#dfitemdump +#dfhotkeynotedump #dffindnameindexes -dfsettlementdump -dfrenamer -#dfvecc -dfcatsplosion +#dfsettlementdump +#dfrenamer +dfvecc +#dfcatsplosion RUNTIME DESTINATION bin ) ENDIF(UNIX) \ No newline at end of file diff --git a/examples/materialtest.cpp b/examples/materialtest.cpp index 2337b3fc9..ba82b2f87 100644 --- a/examples/materialtest.cpp +++ b/examples/materialtest.cpp @@ -1,15 +1,51 @@ +// Just show some position data + #include #include #include #include +#include #include using namespace std; #include #include +#include +#include +#include -int main (void) +void DumpObjStr0Vector (const char * name, DFHack::Process *p, uint32_t addr) { + cout << "----==== " << name << " ====----" << endl; + DFHack::DfVector vect(p,addr,4); + for(int i = 0; i < vect.getSize();i++) + { + uint32_t addr = *(uint32_t *) vect[i]; + cout << p->readSTLString(addr) << endl; + } + cout << endl; +} + +void DumpDWordVector (const char * name, DFHack::Process *p, uint32_t addr) +{ + cout << "----==== " << name << " ====----" << endl; + DFHack::DfVector vect(p,addr,4); + for(int i = 0; i < vect.getSize();i++) + { + uint32_t number = *(uint32_t *) vect[i]; + cout << number << endl; + } + cout << endl; +} + +int main (int numargs, const char ** args) +{ + uint32_t addr; + if (numargs == 2) + { + istringstream input (args[1],istringstream::in); + input >> std::hex >> addr; + } DFHack::API DF("Memory.xml"); try { @@ -24,48 +60,40 @@ int main (void) return 1; } - if(!DF.InitMap()) - { - cerr << "No map loaded, it would be unsafe to enumerate materials" << endl; - #ifndef LINUX_BUILD - cin.ignore(); - #endif - return 1; - } - DF.DestroyMap(); + DFHack::Process* p = DF.getProcess(); + DFHack::memory_info* mem = DF.getMemoryInfo(); + //const vector * names = mem->getClassIDMapping(); - vector Woods; - DF.ReadWoodMatgloss(Woods); + DumpObjStr0Vector("Material templates",p, mem->getAddress("mat_templates")); - vector Plants; - DF.ReadPlantMatgloss(Plants); + DumpObjStr0Vector("Inorganics",p, mem->getAddress("mat_inorganics")); - vector Metals; - DF.ReadMetalMatgloss(Metals); + DumpObjStr0Vector("Organics - all",p, mem->getAddress("mat_organics_all")); - vector Stones; - DF.ReadStoneMatgloss(Stones); + DumpObjStr0Vector("Organics - plants",p, mem->getAddress("mat_organics_plants")); - vector CreatureTypes; - DF.ReadCreatureMatgloss(CreatureTypes); + DumpDWordVector("Maybe map between all organics and plants",p, mem->getAddress("mat_unk1_numbers")); + + DumpObjStr0Vector("Trees/wood",p, mem->getAddress("mat_organics_trees")); + + DumpDWordVector("Maybe map between all organics and trees",p, mem->getAddress("mat_unk2_numbers")); + + DumpObjStr0Vector("Body material templates",p, mem->getAddress("mat_body_material_templates")); + + DumpObjStr0Vector("Body detail plans",p, mem->getAddress("mat_body_detail_plans")); + + DumpObjStr0Vector("Bodies",p, mem->getAddress("mat_bodies")); + + DumpObjStr0Vector("Bodygloss",p, mem->getAddress("mat_bodygloss")); + + DumpObjStr0Vector("Creature variations",p, mem->getAddress("mat_creature_variations")); + + DumpObjStr0Vector("Creature types",p, mem->getAddress("mat_creature_types")); - cout << "Wood: " << Woods[0].id << endl; - cout << "Plant: " << Plants[0].id << endl; - cout << "Metal: " << Metals[0].id << endl; - cout << "Stone: " << Stones[0].id << endl; - cout << "Creature: " << CreatureTypes[0].id << endl; - cout << endl; - cout << "Dumping all stones!" << endl; - for(uint32_t i = 0; i < Stones.size();i++) - { - cout << Stones[i].id << "$" << endl;; - } - DF.Detach(); #ifndef LINUX_BUILD cout << "Done. Press any key to continue" << endl; cin.ignore(); #endif - return 0; } diff --git a/examples/position.cpp b/examples/position.cpp index 05bcc4b87..a2120bac7 100644 --- a/examples/position.cpp +++ b/examples/position.cpp @@ -29,27 +29,20 @@ int main (void) if (DF.InitViewAndCursor()) { int32_t x,y,z; + int32_t width,height; + if(DF.getViewCoords(x,y,z)) cout << "view coords: " << x << "/" << y << "/" << z << endl; if(DF.getCursorCoords(x,y,z)) cout << "cursor coords: " << x << "/" << y << "/" << z << endl; + if(DF.getWindowSize(width,height)) + cout << "window size : " << width << " " << height << endl; } else { cerr << "cursor and window parameters are unsupported on your version of DF" << endl; } - if(DF.InitViewSize()) - { - int32_t width,height; - if(DF.getWindowSize(width,height)) - cout << "window size : " << width << " " << height << endl; - } - else - { - cerr << "view size is unsupported on your version of DF" << endl; - } - if(!DF.Detach()) { cerr << "Can't detach from DF" << endl; diff --git a/output/Memory.xml b/output/Memory.xml index b38aaa0c1..f4c52240e 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -3098,7 +3098,7 @@ Map stuff =========
0x016AD718
- 0x10 + 0x08 0x0092 0x029C
0x16AFE0C
+ SHM initialization (if possible) <-- - g_pProcess->getModuleIndex("Creatures40d",1,d->creature_module); + g_pProcess->getModuleIndex("Creatures2010",1,d->creature_module); if(d->creature_module) { // supply the module with offsets so it can work with them - memcpy(SHMDATA(Creatures::creature_offsets),&d->creatures,sizeof(Creatures::creature_offsets)); - const uint32_t cmd = Creatures::CREATURE_INIT + (d->creature_module << 16); + memcpy(SHMDATA(Creatures2010::creature_offsets),&d->creatures,sizeof(Creatures2010::creature_offsets)); + const uint32_t cmd = Creatures2010::CREATURE_INIT + (d->creature_module << 16); g_pProcess->SetAndWait(cmd); } return true; @@ -106,7 +85,7 @@ bool API::ReadCreature (const int32_t index, t_creature & furball) { // supply the module with offsets so it can work with them SHMCREATURESHDR->index = index; - const uint32_t cmd = Creatures::CREATURE_AT_INDEX + (d->creature_module << 16); + const uint32_t cmd = Creatures2010::CREATURE_AT_INDEX + (d->creature_module << 16); g_pProcess->SetAndWait(cmd); memcpy(&furball,SHMDATA(t_creature),sizeof(t_creature)); // cerr << "creature read from SHM!" << endl; @@ -115,24 +94,25 @@ bool API::ReadCreature (const int32_t index, t_creature & furball) // read pointer from vector at position uint32_t temp = * (uint32_t *) d->p_cre->at (index); furball.origin = temp; - Creatures::creature_offsets &offs = d->creatures; + Creatures2010::creature_offsets &offs = d->creatures; //read creature from memory g_pProcess->read (temp + offs.creature_pos_offset, 3 * sizeof (uint16_t), (uint8_t *) & (furball.x)); // xyz really - g_pProcess->readDWord (temp + offs.creature_type_offset, furball.type); + g_pProcess->readDWord (temp + offs.creature_race_offset, furball.type); g_pProcess->readDWord (temp + offs.creature_flags1_offset, furball.flags1.whole); g_pProcess->readDWord (temp + offs.creature_flags2_offset, furball.flags2.whole); // names d->readName(furball.name,temp + offs.creature_name_offset); - d->readName(furball.squad_name, temp + offs.creature_squad_name_offset); + //d->readName(furball.squad_name, temp + offs.creature_squad_name_offset); d->readName(furball.artifact_name, temp + offs.creature_artifact_name_offset); // custom profession - fill_char_buf (furball.custom_profession, d->p->readSTLString (temp + offs.creature_custom_profession_offset)); + //fill_char_buf (furball.custom_profession, d->p->readSTLString (temp + offs.creature_custom_profession_offset)); // labors g_pProcess->read (temp + offs.creature_labors_offset, NUM_CREATURE_LABORS, furball.labors); // traits - g_pProcess->read (temp + offs.creature_traits_offset, sizeof (uint16_t) * NUM_CREATURE_TRAITS, (uint8_t *) &furball.traits); + //g_pProcess->read (temp + offs.creature_traits_offset, sizeof (uint16_t) * NUM_CREATURE_TRAITS, (uint8_t *) &furball.traits); // learned skills + /* DfVector skills (d->p, temp + offs.creature_skills_offset, 4 ); furball.numSkills = skills.getSize(); for (uint32_t i = 0; i < furball.numSkills;i++) @@ -144,9 +124,11 @@ bool API::ReadCreature (const int32_t index, t_creature & furball) furball.skills[i].rating = g_pProcess->readByte (temp2 + 4); furball.skills[i].experience = g_pProcess->readWord (temp2 + 8); } + */ // profession furball.profession = g_pProcess->readByte (temp + offs.creature_profession_offset); // current job HACK: the job object isn't cleanly represented here + /* uint32_t jobIdAddr = g_pProcess->readDWord (temp + offs.creature_current_job_offset); if (jobIdAddr) @@ -158,8 +140,9 @@ bool API::ReadCreature (const int32_t index, t_creature & furball) { furball.current_job.active = false; } - +*/ //likes + /* DfVector likes(d->p, temp + offs.creature_likes_offset, 4); furball.numLikes = likes.getSize(); for(uint32_t i = 0;ireadWord (temp + offs.creature_mood_offset); - +*/ g_pProcess->readDWord (temp + offs.creature_happiness_offset, furball.happiness); g_pProcess->readDWord (temp + offs.creature_id_offset, furball.id); + /* g_pProcess->readDWord (temp + offs.creature_agility_offset, furball.agility); g_pProcess->readDWord (temp + offs.creature_strength_offset, furball.strength); g_pProcess->readDWord (temp + offs.creature_toughness_offset, furball.toughness); g_pProcess->readDWord (temp + offs.creature_money_offset, furball.money); furball.squad_leader_id = (int32_t) g_pProcess->readDWord (temp + offs.creature_squad_leader_id_offset); + */ g_pProcess->readByte (temp + offs.creature_sex_offset, furball.sex); - +/* g_pProcess->readDWord(temp + offs.creature_pregnancy_offset, furball.pregnancy_timer); furball.blood_max = (int32_t) g_pProcess->readDWord(temp + offs.creature_blood_max_offset); furball.blood_current = (int32_t) g_pProcess->readDWord(temp + offs.creature_blood_current_offset); g_pProcess->readDWord(temp + offs.creature_bleed_offset, furball.bleed_rate); - +*/ return true; } - +/* bool API::WriteLabors(const uint32_t index, uint8_t labors[NUM_CREATURE_LABORS]) { if(!d->creaturesInited) return false; uint32_t temp = * (uint32_t *) d->p_cre->at (index); WriteRaw(temp + d->creatures.creature_labors_offset, NUM_CREATURE_LABORS, labors); } - - +*/ +/* bool API::getCurrentCursorCreature(uint32_t & creature_index) { if(!d->cursorWindowInited) return false; creature_index = g_pProcess->readDWord(d->current_cursor_creature_offset); return true; } -*/ \ No newline at end of file +*/ +void API::FinishReadCreatures() +{ + if(d->p_cre) + { + delete d->p_cre; + d->p_cre = 0; + } + d->creaturesInited = false; + //FinishReadNameTables(); +} \ No newline at end of file diff --git a/dfhack/modules/Materials.cpp b/dfhack/modules/Materials.cpp index bdd1ba2d9..b3ee08f2a 100644 --- a/dfhack/modules/Materials.cpp +++ b/dfhack/modules/Materials.cpp @@ -188,9 +188,16 @@ bool API::ReadPlantMaterials (vector & plants) { return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("mat_organics_plants"), plants ); } - +/* +Gives bad results combined with the creature race field! bool API::ReadCreatureTypes (vector & creatures) { return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("mat_creature_types"), creatures ); return true; } +*/ +bool API::ReadCreatureTypes (vector & creatures) +{ + return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("creature_type_vector"), creatures ); + return true; +} diff --git a/dfhack/private/APIPrivate.h b/dfhack/private/APIPrivate.h index 6c3752a7f..930691ad0 100644 --- a/dfhack/private/APIPrivate.h +++ b/dfhack/private/APIPrivate.h @@ -33,7 +33,8 @@ distribution. #include #include #include -#include +// #include +#include #define SHMCMD(num) ((shm_cmd *)d->shm_start)[num]->pingpong #define SHMHDR ((shm_core_hdr *)d->shm_start) diff --git a/dfhack/shm/mod-creature2010.h b/dfhack/shm/mod-creature2010.h new file mode 100644 index 000000000..620d21f9d --- /dev/null +++ b/dfhack/shm/mod-creature2010.h @@ -0,0 +1,87 @@ +/* +www.sourceforge.net/projects/dfhack +Copyright (c) 2009 Petr Mrázek (peterix) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef MOD_CREATURES2010_H +#define MOD_CREATURES2010_H + +namespace DFHack +{ + namespace Creatures2010 + { + +#define CREATURES2010_VERSION 1 +typedef struct +{ + // creature offsets + uint32_t creature_vector; + uint32_t creature_pos_offset; + uint32_t creature_profession_offset; + uint32_t creature_race_offset; + uint32_t creature_flags1_offset; + uint32_t creature_flags2_offset; + uint32_t creature_name_offset; + uint32_t creature_sex_offset; + uint32_t creature_id_offset; + uint32_t creature_labors_offset; + uint32_t creature_happiness_offset; + uint32_t creature_artifact_name_offset; + // name offsets (needed for reading creature names) + uint32_t name_firstname_offset; + uint32_t name_nickname_offset; + uint32_t name_words_offset; +} creature_offsets; + +typedef struct +{ + bool inited; + creature_offsets offsets; +} creature_modulestate; + +typedef struct +{ + shm_cmd cmd[SHM_MAX_CLIENTS]; // MANDATORY! + // box + uint32_t x; + uint32_t y; + uint32_t z; + uint32_t x2; + uint32_t y2; + uint32_t z2; + // starting index + int32_t index; +} shm_creature_hdr; + +enum CREATURE_COMMAND +{ + CREATURE_INIT = 0, // initialization + CREATURE_FIND_IN_BOX, + CREATURE_AT_INDEX, + NUM_CREATURE_CMDS, +}; +DFPP_module Init(void); + + } +} + +#endif \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4569dd33f..e430b94b0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -18,8 +18,8 @@ ADD_EXECUTABLE(dfexpbench expbench.cpp) TARGET_LINK_LIBRARIES(dfexpbench dfhack) # creaturedump - basic creature dump - a test of the creature related exports -# ADD_EXECUTABLE(dfcreaturedump creaturedump.cpp) -# TARGET_LINK_LIBRARIES(dfcreaturedump dfhack) +ADD_EXECUTABLE(dfcreaturedump creaturedump.cpp) +TARGET_LINK_LIBRARIES(dfcreaturedump dfhack) # materialtest - just list the first material of each type ADD_EXECUTABLE(dfmaterialtest materialtest.cpp) diff --git a/examples/creaturedump.cpp b/examples/creaturedump.cpp index 125f802dc..73d9afdcc 100644 --- a/examples/creaturedump.cpp +++ b/examples/creaturedump.cpp @@ -43,8 +43,8 @@ vector< vector > itemTypes; DFHack::memory_info *mem; vector< vector > englishWords; vector< vector > foreignWords; - -likeType printLike(DFHack::t_like like, const matGlosses & mat,const vector< vector > & itemTypes) +/* +likeType printLike40d(DFHack::t_like like, const matGlosses & mat,const vector< vector > & itemTypes) { // The function in DF which prints out the likes is a monster, it is a huge switch statement with tons of options and calls a ton of other functions as well, //so I am not going to try and put all the possibilites here, only the low hanging fruit, with stones and metals, as well as items, //you can easily find good canidates for military duty for instance @@ -147,7 +147,7 @@ likeType printLike(DFHack::t_like like, const matGlosses & mat,const vector< vec } return(FAIL); } - +*/ void printCreature(DFHack::API & DF, const DFHack::t_creature & creature) { @@ -163,13 +163,15 @@ void printCreature(DFHack::API & DF, const DFHack::t_creature & creature) cout << ", nick name: " << creature.name.nickname; addendl = true; } + /* string transName = DF.TranslateName(creature.name,englishWords,foreignWords,false); if(!transName.empty()) { cout << ", trans name: " << transName; addendl=true; } - +*/ + /* cout << ", likes: "; for(uint32_t i = 0;igetProfession(creature.profession) << "(" << (int) creature.profession << ")"; + cout << "profession: " /*<< mem->getProfession(creature.profession) <<*/ "(" << (int) creature.profession << ")"; + /* if(creature.custom_profession[0]) { cout << ", custom profession: " << creature.custom_profession; @@ -192,16 +196,18 @@ void printCreature(DFHack::API & DF, const DFHack::t_creature & creature) { cout << ", current job: " << mem->getJob(creature.current_job.jobId); } + */ cout << endl; - cout << "happiness: " << creature.happiness << ", strength: " << creature.strength << ", agility: " - << creature.agility << ", toughness: " << creature.toughness << ", money: " << creature.money << ", id: " << creature.id; + cout << "happiness: " << creature.happiness /*<< ", strength: " << creature.strength << ", agility: " + << creature.agility << ", toughness: " << creature.toughness << ", money: " << creature.money*/ << ", id: " << creature.id; + /* if(creature.squad_leader_id != -1) { cout << ", squad_leader_id: " << creature.squad_leader_id; } if(creature.mood != -1){ cout << ", mood: " << creature.mood << " "; - } + }*/ cout << ", sex: "; if(creature.sex == 0) { @@ -212,10 +218,11 @@ void printCreature(DFHack::API & DF, const DFHack::t_creature & creature) cout <<"Male"; } cout << endl; - + /* if(creature.pregnancy_timer > 0) cout << "gives birth in " << creature.pregnancy_timer/1200 << " days. "; cout << "Blood: " << creature.blood_current << "/" << creature.blood_max << " bleeding: " << creature.bleed_rate; + */ cout << endl; /* @@ -288,10 +295,12 @@ void printCreature(DFHack::API & DF, const DFHack::t_creature & creature) cout << "from the underworld, "; } cout << endl; - if(creature.flags1.bits.had_mood && (creature.mood == -1 || creature.mood == 8 ) ){ + /* + if(creature.flags1.bits.had_mood && (creature.mood == -1 || creature.mood == 8 ) ) + { string artifact_name = DF.TranslateName(creature.artifact_name,englishWords,foreignWords,false); cout << "artifact: " << artifact_name << endl; - } + }*/ cout << endl; } @@ -329,28 +338,29 @@ int main (void) #endif return 1; } - + /* DF.ReadItemTypes(itemTypes); DF.ReadPlantMatgloss(mat.plantMat); DF.ReadWoodMatgloss(mat.woodMat); DF.ReadStoneMatgloss(mat.stoneMat); DF.ReadMetalMatgloss(mat.metalMat); DF.ReadCreatureMatgloss(mat.creatureMat); - + */ mem = DF.getMemoryInfo(); // get stone matgloss mapping - if(!DF.ReadCreatureMatgloss(creaturestypes)) + if(!DF.ReadCreatureTypes(creaturestypes)) { cerr << "Can't get the creature types." << endl; return 1; } - + /* if(!DF.InitReadNameTables(englishWords,foreignWords)) { cerr << "Can't get name tables" << endl; return 1; } - DF.InitViewAndCursor(); + */ + //DF.InitViewAndCursor(); for(uint32_t i = 0; i < numCreatures; i++) { DFHack::t_creature temp; @@ -361,6 +371,7 @@ int main (void) printCreature(DF,temp); } } + /* uint32_t currentIdx; DFHack::t_creature currentCreature; DF.getCurrentCursorCreature(currentIdx); @@ -368,6 +379,7 @@ int main (void) DF.ReadCreature(currentIdx, currentCreature); printCreature(DF,currentCreature); + */ DF.FinishReadCreatures(); DF.Detach(); #ifndef LINUX_BUILD diff --git a/output/Memory.xml b/output/Memory.xml index 933a4a8a1..227c66a0e 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -3132,6 +3132,8 @@ =========
0x0166ecc4
0x0 + 0x88 + 0x8C 0x90 0xF8 0xFC @@ -3174,8 +3176,10 @@
0x16AFDDC
0x16AFDF4
- +
0x16AFE0C
+ +
0x016AFE58
+ 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + 40 + 41 + 42 + 43 + 44 + 45 + 46 + 47 + 48 + 49 + 50 + 51 + 52 + 53 + 54 + 55 + 56 + 57 + 60 + 62 + 63 + 64 + 65 + 66 + 58 + 59 + 61 + 67 + 68 + 69 + 70 + 71 + 72 + 73 + 74 + 75 + 76 + 77 + 78 + 79 + 80 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 90 + 91 + 92 + 93 + 94 + 95 + 96 + 97 + 98 + 88 + 89 + 90 ==================================================================== J O B S ==================================================================== From e42bf9265478b308f371d85400b99f60aa3f3784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 4 Apr 2010 15:12:06 +0200 Subject: [PATCH 25/29] Disable SHM stuff in Maps --- dfhack/modules/Maps.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dfhack/modules/Maps.cpp b/dfhack/modules/Maps.cpp index 75008851e..d24999ff8 100644 --- a/dfhack/modules/Maps.cpp +++ b/dfhack/modules/Maps.cpp @@ -57,6 +57,7 @@ bool API::InitMap() /* * --> SHM initialization (if possible) <-- */ + /* g_pProcess->getModuleIndex("Maps2010",1,d->maps_module); if(d->maps_module) @@ -77,7 +78,7 @@ bool API::InitMap() g_pProcess->SetAndWait(cmd); //cerr << "Map acceleration enabled!" << endl; } - + */ // get the map pointer uint32_t x_array_loc = g_pProcess->readDWord (map_offset); if (!x_array_loc) From d080e73ac2cf59f0913f7346f35fc8cdbee95ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 4 Apr 2010 15:24:50 +0200 Subject: [PATCH 26/29] Son't build shm --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c7a474ea..84930eaca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ include_directories (${CMAKE_SOURCE_DIR}/dfhack/depends/tinyxml/) include_directories (${CMAKE_SOURCE_DIR}/dfhack/depends/argstream/) add_subdirectory (dfhack) -add_subdirectory (dfhack/shm) +#add_subdirectory (dfhack/shm) #FIXME: add exports for MSVC #add_subdirectory (dfhack/depends/md5) #add_subdirectory (dfhack/depends/tinyxml) From 500ee6a49bc14dc19a1803b5fae23e75284a97a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 5 Apr 2010 00:48:19 +0200 Subject: [PATCH 27/29] Modular API --- dfhack/APIPrivate.cpp | 41 ++++-- dfhack/DFHackAPI.cpp | 102 +++++++------- dfhack/DFMemInfo.cpp | 23 +-- dfhack/DFMemInfoManager.cpp | 4 + dfhack/DFProcess-linux-SHM.cpp | 5 + dfhack/DFProcess-linux-wine.cpp | 4 + dfhack/DFProcess-linux.cpp | 4 + dfhack/DFProcess-windows-SHM.cpp | 4 + dfhack/DFProcess-windows.cpp | 4 + dfhack/DFProcessEnumerator-linux.cpp | 4 + dfhack/DFProcessEnumerator-windows.cpp | 4 + dfhack/DFVector.cpp | 3 + dfhack/DFWindow-linux.cpp | 1 + dfhack/DFWindow-windows.cpp | 2 + dfhack/include/DFCommonInternal.h | 4 +- dfhack/include/DFHackAPI.h | 18 ++- dfhack/include/DFTypes.h | 36 ++--- dfhack/include/modules/Creatures.h | 33 +++++ dfhack/include/modules/Gui.h | 34 +++++ dfhack/include/modules/Maps.h | 109 +++++++++++++++ dfhack/include/modules/Materials.h | 53 +++++++ dfhack/include/modules/Position.h | 35 +++++ dfhack/modules/Creatures-data.h | 4 - dfhack/modules/Creatures-proc.h | 15 -- dfhack/modules/Creatures.cpp | 186 ++++++++++++++++++------- dfhack/modules/Gui-data.h | 4 - dfhack/modules/Gui-proc.h | 10 -- dfhack/modules/Gui.cpp | 59 +++++++- dfhack/modules/Maps-data.h | 17 --- dfhack/modules/Maps-proc.h | 80 ----------- dfhack/modules/Maps.cpp | 179 ++++++++++++++---------- dfhack/modules/Materials-data.h | 0 dfhack/modules/Materials-proc.h | 13 -- dfhack/modules/Materials.cpp | 23 ++- dfhack/modules/Position-data.h | 6 - dfhack/modules/Position-proc.h | 17 --- dfhack/modules/Position.cpp | 76 +++++++--- dfhack/private/APIPrivate.h | 157 +++++++++++---------- dfhack/shm/mod-creature2010.h | 2 +- dfhack/shm/mod-creature40d.h | 5 +- dfhack/shm/mod-maps.h | 8 +- dfhack/shm/shms.h | 2 +- examples/creaturedump.cpp | 13 +- examples/expbench.cpp | 14 +- examples/materialtest.cpp | 9 +- examples/position.cpp | 12 +- examples/veinlook.cpp | 42 +++--- tools/prospector.cpp | 20 ++- tools/reveal.cpp | 12 +- 49 files changed, 942 insertions(+), 570 deletions(-) create mode 100644 dfhack/include/modules/Creatures.h create mode 100644 dfhack/include/modules/Gui.h create mode 100644 dfhack/include/modules/Maps.h create mode 100644 dfhack/include/modules/Materials.h create mode 100644 dfhack/include/modules/Position.h delete mode 100644 dfhack/modules/Creatures-data.h delete mode 100644 dfhack/modules/Creatures-proc.h delete mode 100644 dfhack/modules/Gui-data.h delete mode 100644 dfhack/modules/Gui-proc.h delete mode 100644 dfhack/modules/Maps-data.h delete mode 100644 dfhack/modules/Maps-proc.h delete mode 100644 dfhack/modules/Materials-data.h delete mode 100644 dfhack/modules/Materials-proc.h delete mode 100644 dfhack/modules/Position-data.h delete mode 100644 dfhack/modules/Position-proc.h diff --git a/dfhack/APIPrivate.cpp b/dfhack/APIPrivate.cpp index a45be66ec..37b2ce8da 100644 --- a/dfhack/APIPrivate.cpp +++ b/dfhack/APIPrivate.cpp @@ -3,30 +3,45 @@ #include #include #include -using namespace DFHack; - #include "private/APIPrivate.h" +#include "DFMemInfo.h" +#include "DFProcess.h" + +#include "modules/Creatures.h" +#include "modules/Maps.h" +#include "modules/Materials.h" +#include "modules/Position.h" +#include "modules/Gui.h" + +using namespace DFHack; APIPrivate::APIPrivate() { + // init modules + creatures = 0; + maps = 0; + position = 0; + gui = 0; + materials = 0; +} + +APIPrivate::~APIPrivate() +{ + if(creatures) delete creatures; + if(maps) delete maps; + if(position) delete position; + if(gui) delete gui; + if(materials) delete materials; } bool APIPrivate::InitReadNames() { - try - { - name_firstname_offset = offset_descriptor->getOffset("name_firstname"); - name_nickname_offset = offset_descriptor->getOffset("name_nickname"); - name_words_offset = offset_descriptor->getOffset("name_words"); - } - catch(Error::MissingMemoryDefinition) - { - return false; - } + name_firstname_offset = offset_descriptor->getOffset("name_firstname"); + name_nickname_offset = offset_descriptor->getOffset("name_nickname"); + name_words_offset = offset_descriptor->getOffset("name_words"); return true; } - void APIPrivate::readName(t_name & name, uint32_t address) { g_pProcess->readSTLString(address + name_firstname_offset , name.first_name, 128); diff --git a/dfhack/DFHackAPI.cpp b/dfhack/DFHackAPI.cpp index 3655cb9bb..44879c0f4 100644 --- a/dfhack/DFHackAPI.cpp +++ b/dfhack/DFHackAPI.cpp @@ -23,14 +23,26 @@ distribution. */ #include "DFCommonInternal.h" + +#include "DFProcess.h" +#include "DFProcessEnumerator.h" +#include "DFHackAPI.h" +#include "DFError.h" + #include #include #include #include -using namespace DFHack; - #include "private/APIPrivate.h" +#include "modules/Maps.h" +#include "modules/Materials.h" +#include "modules/Position.h" +#include "modules/Gui.h" +#include "modules/Creatures.h" + +using namespace DFHack; + API::API (const string path_to_xml) : d (new APIPrivate()) { @@ -151,6 +163,44 @@ DFWindow * API::getWindow() return d->p->getWindow(); } +/******************************************************************************* + M O D U L E S +*******************************************************************************/ +Creatures * API::getCreatures() +{ + if(!d->creatures) + d->creatures = new Creatures(d); + return d->creatures; +} + +Maps * API::getMaps() +{ + if(!d->maps) + d->maps = new Maps(d); + return d->maps; +} + +Gui * API::getGui() +{ + if(!d->gui) + d->gui = new Gui(d); + return d->gui; +} + +Position * API::getPosition() +{ + if(!d->position) + d->position = new Position(d); + return d->position; +} + +Materials * API::getMaterials() +{ + if(!d->materials) + d->materials = new Materials(d); + return d->materials; +} + /* // returns number of buildings, expects v_buildingtypes that will later map t_building.type to its name bool API::InitReadBuildings ( uint32_t& numbuildings ) @@ -504,53 +554,7 @@ bool API::ReadHotkeys(t_hotkey hotkeys[]) } return true; } -// returns index of creature actually read or -1 if no creature can be found -int32_t API::ReadCreatureInBox (int32_t index, t_creature & furball, - const uint16_t x1, const uint16_t y1, const uint16_t z1, - const uint16_t x2, const uint16_t y2, const uint16_t z2) -{ - if (!d->creaturesInited) return -1; - if(d->creature_module) - { - // supply the module with offsets so it can work with them - SHMCREATURESHDR->index = index; - SHMCREATURESHDR->x = x1; - SHMCREATURESHDR->y = y1; - SHMCREATURESHDR->z = z1; - SHMCREATURESHDR->x2 = x2; - SHMCREATURESHDR->y2 = y2; - SHMCREATURESHDR->z2 = z2; - const uint32_t cmd = Creatures::CREATURE_FIND_IN_BOX + (d->creature_module << 16); - g_pProcess->SetAndWait(cmd); - if(SHMCREATURESHDR->index != -1) - memcpy(&furball,SHMDATA(void),sizeof(t_creature)); - return SHMCREATURESHDR->index; - } - else - { - uint16_t coords[3]; - uint32_t size = d->p_cre->getSize(); - while (uint32_t(index) < size) - { - // read pointer from vector at position - uint32_t temp = * (uint32_t *) d->p_cre->at (index); - g_pProcess->read (temp + d->creatures.creature_pos_offset, 3 * sizeof (uint16_t), (uint8_t *) &coords); - if (coords[0] >= x1 && coords[0] < x2) - { - if (coords[1] >= y1 && coords[1] < y2) - { - if (coords[2] >= z1 && coords[2] < z2) - { - ReadCreature (index, furball); - return index; - } - } - } - index++; - } - return -1; - } -} + bool API::getItemIndexesInBox(vector &indexes, const uint16_t x1, const uint16_t y1, const uint16_t z1, diff --git a/dfhack/DFMemInfo.cpp b/dfhack/DFMemInfo.cpp index 1b3050c58..f15f88c70 100644 --- a/dfhack/DFMemInfo.cpp +++ b/dfhack/DFMemInfo.cpp @@ -23,25 +23,10 @@ distribution. */ #include "DFCommonInternal.h" -/* -#if !defined(NDEBUG) -#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING -#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE -#endif -// really, we don't need it -#define BOOST_MULTI_INDEX_DISABLE_SERIALIZATION - -#include -#include -#include -#include -#include -#include -#include - -using boost::multi_index_container; -using namespace boost::multi_index; -*/ +#include "DFMemInfo.h" +#include "DFError.h" +#include "DFProcess.h" + using namespace DFHack; /* diff --git a/dfhack/DFMemInfoManager.cpp b/dfhack/DFMemInfoManager.cpp index 68060e9ef..3059eca66 100644 --- a/dfhack/DFMemInfoManager.cpp +++ b/dfhack/DFMemInfoManager.cpp @@ -23,6 +23,10 @@ distribution. */ #include "DFCommonInternal.h" +#include "DFMemInfo.h" +#include "DFMemInfoManager.h" +#include "DFError.h" + using namespace DFHack; MemInfoManager::~MemInfoManager() diff --git a/dfhack/DFProcess-linux-SHM.cpp b/dfhack/DFProcess-linux-SHM.cpp index d590dbb22..113149a65 100644 --- a/dfhack/DFProcess-linux-SHM.cpp +++ b/dfhack/DFProcess-linux-SHM.cpp @@ -22,6 +22,11 @@ must not be misrepresented as being the original software. distribution. */ #include "DFCommonInternal.h" +#include "DFProcess.h" +#include "DFWindow.h" +#include "DFMemInfo.h" +#include "DFError.h" + #include #include #include diff --git a/dfhack/DFProcess-linux-wine.cpp b/dfhack/DFProcess-linux-wine.cpp index 89e9f3d2f..253fef3a1 100644 --- a/dfhack/DFProcess-linux-wine.cpp +++ b/dfhack/DFProcess-linux-wine.cpp @@ -22,6 +22,10 @@ must not be misrepresented as being the original software. distribution. */ #include "DFCommonInternal.h" +#include "DFProcess.h" +#include "DFWindow.h" +#include "DFMemInfo.h" +#include "DFError.h" #include #include #include diff --git a/dfhack/DFProcess-linux.cpp b/dfhack/DFProcess-linux.cpp index 616e3fd62..e2bf16af6 100644 --- a/dfhack/DFProcess-linux.cpp +++ b/dfhack/DFProcess-linux.cpp @@ -22,6 +22,10 @@ must not be misrepresented as being the original software. distribution. */ #include "DFCommonInternal.h" +#include "DFProcess.h" +#include "DFWindow.h" +#include "DFMemInfo.h" +#include "DFError.h" #include #include using namespace DFHack; diff --git a/dfhack/DFProcess-windows-SHM.cpp b/dfhack/DFProcess-windows-SHM.cpp index 33a1ceb7b..8e5bf0920 100644 --- a/dfhack/DFProcess-windows-SHM.cpp +++ b/dfhack/DFProcess-windows-SHM.cpp @@ -22,6 +22,10 @@ must not be misrepresented as being the original software. distribution. */ #include "DFCommonInternal.h" +#include "DFProcess.h" +#include "DFWindow.h" +#include "DFMemInfo.h" +#include "DFError.h" #include "shms.h" #include "mod-core.h" using namespace DFHack; diff --git a/dfhack/DFProcess-windows.cpp b/dfhack/DFProcess-windows.cpp index 6bb5acca9..ce3e0a6cf 100644 --- a/dfhack/DFProcess-windows.cpp +++ b/dfhack/DFProcess-windows.cpp @@ -22,6 +22,10 @@ must not be misrepresented as being the original software. distribution. */ #include "DFCommonInternal.h" +#include "DFProcess.h" +#include "DFWindow.h" +#include "DFMemInfo.h" +#include "DFError.h" using namespace DFHack; class NormalProcess::Private diff --git a/dfhack/DFProcessEnumerator-linux.cpp b/dfhack/DFProcessEnumerator-linux.cpp index 6efb57e68..19103c1f3 100644 --- a/dfhack/DFProcessEnumerator-linux.cpp +++ b/dfhack/DFProcessEnumerator-linux.cpp @@ -23,6 +23,10 @@ distribution. */ #include "DFCommonInternal.h" +#include "DFProcessEnumerator.h" +#include "DFProcess.h" +#include "DFMemInfo.h" +#include "DFMemInfoManager.h" #include #include #include diff --git a/dfhack/DFProcessEnumerator-windows.cpp b/dfhack/DFProcessEnumerator-windows.cpp index 7e3c095e8..da42ebd50 100644 --- a/dfhack/DFProcessEnumerator-windows.cpp +++ b/dfhack/DFProcessEnumerator-windows.cpp @@ -23,6 +23,10 @@ distribution. */ #include "DFCommonInternal.h" +#include "DFProcessEnumerator.h" +#include "DFProcess.h" +#include "DFMemInfo.h" +#include "DFMemInfoManager.h" using namespace DFHack; /// HACK: global variables (only one process can be attached at the same time.) diff --git a/dfhack/DFVector.cpp b/dfhack/DFVector.cpp index 4123940ca..61c33c30c 100644 --- a/dfhack/DFVector.cpp +++ b/dfhack/DFVector.cpp @@ -24,6 +24,9 @@ distribution. #include "Tranquility.h" #include "DFCommonInternal.h" +#include "DFVector.h" +#include "DFMemInfo.h" +#include "DFProcess.h" using namespace DFHack; diff --git a/dfhack/DFWindow-linux.cpp b/dfhack/DFWindow-linux.cpp index 99880da44..bffe99de6 100644 --- a/dfhack/DFWindow-linux.cpp +++ b/dfhack/DFWindow-linux.cpp @@ -22,6 +22,7 @@ must not be misrepresented as being the original software. distribution. */ #include "DFCommonInternal.h" +#include "DFWindow.h" #include //need for X11 functions #include diff --git a/dfhack/DFWindow-windows.cpp b/dfhack/DFWindow-windows.cpp index 1fc100555..78e4ffc62 100644 --- a/dfhack/DFWindow-windows.cpp +++ b/dfhack/DFWindow-windows.cpp @@ -23,6 +23,8 @@ distribution. */ #include "DFCommonInternal.h" +#include "DFWindow.h" +#include "DFProcess.h" using namespace DFHack; // should always reflect the enum in DFkeys.h diff --git a/dfhack/include/DFCommonInternal.h b/dfhack/include/DFCommonInternal.h index 394333ea5..255dde145 100644 --- a/dfhack/include/DFCommonInternal.h +++ b/dfhack/include/DFCommonInternal.h @@ -77,6 +77,7 @@ namespace DFHack # define BUILD_DFHACK_LIB #endif +/* #include "DFTypes.h" //#include "DFDataModel.h" #include "DFProcess.h" @@ -86,13 +87,14 @@ namespace DFHack #include "DFVector.h" #include "DFMemInfo.h" #include "DFError.h" +*/ #include #include #include #include -#include "DFHackAPI.h" +//#include "DFHackAPI.h" #define _QUOTEME(x) #x #define QUOT(x) _QUOTEME(x) diff --git a/dfhack/include/DFHackAPI.h b/dfhack/include/DFHackAPI.h index 70416fa2c..6349f083d 100644 --- a/dfhack/include/DFHackAPI.h +++ b/dfhack/include/DFHackAPI.h @@ -40,6 +40,14 @@ namespace DFHack class APIPrivate; class memory_info; class Process; + + // modules + class Maps; + class Creatures; + class Position; + class Gui; + class Materials; + class DFHACK_EXPORT API { APIPrivate * const d; @@ -76,11 +84,11 @@ namespace DFHack void ReadRaw (const uint32_t offset, const uint32_t size, uint8_t *target); void WriteRaw (const uint32_t offset, const uint32_t size, uint8_t *source); - #include "../modules/Position-proc.h" - #include "../modules/Gui-proc.h" - #include "../modules/Maps-proc.h" - #include "../modules/Materials-proc.h" - #include "../modules/Creatures-proc.h" + Creatures * getCreatures(); + Maps * getMaps(); + Gui * getGui(); + Position * getPosition(); + Materials * getMaterials(); /* * Constructions (costructed walls, floors, ramps, etc...) diff --git a/dfhack/include/DFTypes.h b/dfhack/include/DFTypes.h index daa713039..f9ba70cd5 100644 --- a/dfhack/include/DFTypes.h +++ b/dfhack/include/DFTypes.h @@ -53,26 +53,6 @@ struct junk_fill */ }; -struct t_matgloss -{ - char id[128]; //the id in the raws - uint8_t fore; // Annoyingly the offset for this differs between types - uint8_t back; - uint8_t bright; - char name[128]; //this is the name displayed ingame -}; - -struct t_matglossPlant -{ - char id[128]; //the id in the raws - uint8_t fore; // Annoyingly the offset for this differs between types - uint8_t back; - uint8_t bright; - char name[128]; //this is the name displayed ingame - char drink_name[128]; //the name this item becomes a drink - char food_name[128]; - char extract_name[128]; -}; /* struct t_vein { @@ -548,12 +528,12 @@ CREATURE //#pragma pack(push,4) struct t_name { - char first_name[128]; - char nickname[128]; - int32_t words[7]; - uint16_t parts_of_speech[7]; - uint32_t language; - bool has_name; + char first_name[128]; + char nickname[128]; + int32_t words[7]; + uint16_t parts_of_speech[7]; + uint32_t language; + bool has_name; }; struct t_skill @@ -589,8 +569,8 @@ struct t_creature t_creaturflags1 flags1; t_creaturflags2 flags2; t_name name; - t_name squad_name; - t_name artifact_name; + t_name squad_name; + t_name artifact_name; uint8_t profession; char custom_profession[128]; // enabled labors diff --git a/dfhack/include/modules/Creatures.h b/dfhack/include/modules/Creatures.h new file mode 100644 index 000000000..a701eaef7 --- /dev/null +++ b/dfhack/include/modules/Creatures.h @@ -0,0 +1,33 @@ +#ifndef CL_MOD_CREATURES +#define CL_MOD_CREATURES +/* +* Creatures +*/ +#include "Export.h" +namespace DFHack +{ + class APIPrivate; + struct t_creature; + class DFHACK_EXPORT Creatures + { + public: + Creatures(DFHack::APIPrivate * d); + ~Creatures(); + bool Start( uint32_t & numCreatures); + bool Finish(); + + // Read creatures in a box, starting with index. Returns -1 if no more creatures + // found. Call repeatedly do get all creatures in a specified box (uses tile coords) + int32_t ReadCreatureInBox(const int32_t index, t_creature & furball, + const uint16_t x1, const uint16_t y1,const uint16_t z1, + const uint16_t x2, const uint16_t y2,const uint16_t z2); + bool ReadCreature(const int32_t index, t_creature & furball); + /// write labors of a creature (for Dwarf Therapist) + //bool WriteLabors(const uint32_t index, uint8_t labors[NUM_CREATURE_LABORS]); + + private: + struct Private; + Private *d; + }; +} +#endif \ No newline at end of file diff --git a/dfhack/include/modules/Gui.h b/dfhack/include/modules/Gui.h new file mode 100644 index 000000000..ab342e77a --- /dev/null +++ b/dfhack/include/modules/Gui.h @@ -0,0 +1,34 @@ +#ifndef CL_MOD_GUI +#define CL_MOD_GUI + +/* +* Gui: Query the DF's GUI state +*/ +#include "Export.h" + +namespace DFHack +{ + class APIPrivate; + struct t_viewscreen; + class DFHACK_EXPORT Gui + { + public: + + Gui(DFHack::APIPrivate * d); + ~Gui(); + bool Start(); + bool Finish(); + + ///true if paused, false if not + bool ReadPauseState(); + /// read the DF menu view state (stock screen, unit screen, other screens + bool ReadViewScreen(t_viewscreen &); + /// read the DF menu state (designation menu ect) + uint32_t ReadMenuState(); + + private: + struct Private; + Private *d; + }; +} +#endif \ No newline at end of file diff --git a/dfhack/include/modules/Maps.h b/dfhack/include/modules/Maps.h new file mode 100644 index 000000000..eb4856d32 --- /dev/null +++ b/dfhack/include/modules/Maps.h @@ -0,0 +1,109 @@ +#ifndef CL_MOD_MAPS +#define CL_MOD_MAPS + +#include "Export.h" +/* +* Maps: Read and write DF's map +*/ +namespace DFHack +{ + class APIPrivate; + struct t_viewscreen; + class DFHACK_EXPORT Maps + { + public: + + Maps(DFHack::APIPrivate * d); + ~Maps(); + bool Start(); + bool Finish(); + + // read region surroundings, get their vectors of geolayers so we can do translation (or just hand the translation table to the client) + // returns an array of 9 vectors of indices into stone matgloss + /** + Method for reading the geological surrounding of the currently loaded region. + assign is a reference to an array of nine vectors of unsigned words that are to be filled with the data + array is indexed by the BiomeOffset enum + + I omitted resolving the layer matgloss in this API, because it would + introduce overhead by calling some method for each tile. You have to do it + yourself. First get the stuff from ReadGeology and then for each block get + the RegionOffsets. For each tile get the real region from RegionOffsets and + cross-reference it with the geology stuff (region -- array of vectors, depth -- + vector). I'm thinking about turning that Geology stuff into a + two-dimensional array with static size. + + this is the algorithm for applying matgloss: + void DfMap::applyGeoMatgloss(Block * b) + { + // load layer matgloss + for(int x_b = 0; x_b < BLOCK_SIZE; x_b++) + { + for(int y_b = 0; y_b < BLOCK_SIZE; y_b++) + { + int geolayer = b->designation[x_b][y_b].bits.geolayer_index; + int biome = b->designation[x_b][y_b].bits.biome; + b->material[x_b][y_b].type = Mat_Stone; + b->material[x_b][y_b].index = v_geology[b->RegionOffsets[biome]][geolayer]; + } + } + } + */ + bool ReadGeology( std::vector < std::vector >& assign ); + + /* + * BLOCK DATA + */ + /* + /// allocate and read pointers to map blocks + bool InitMap(); + /// destroy the mapblock cache + bool DestroyMap(); + */ + /// get size of the map in tiles + void getSize(uint32_t& x, uint32_t& y, uint32_t& z); + + /** + * Return false/0 on failure, buffer allocated by client app, 256 items long + */ + bool isValidBlock(uint32_t blockx, uint32_t blocky, uint32_t blockz); + /** + * Get the address of a block or 0 if block is not valid + */ + uint32_t getBlockPtr (uint32_t blockx, uint32_t blocky, uint32_t blockz); + + /// read the whole map block at block coords (see DFTypes.h for the block structure) + bool ReadBlock40d(uint32_t blockx, uint32_t blocky, uint32_t blockz, mapblock40d * buffer); + + /// read/write block tile types + bool ReadTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, tiletypes40d *buffer); + bool WriteTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, tiletypes40d *buffer); + + /// read/write block designations + bool ReadDesignations(uint32_t blockx, uint32_t blocky, uint32_t blockz, designations40d *buffer); + bool WriteDesignations (uint32_t blockx, uint32_t blocky, uint32_t blockz, designations40d *buffer); + + /// read/write block occupancies + bool ReadOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, occupancies40d *buffer); + bool WriteOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, occupancies40d *buffer); + + /// read/write the block dirty bit - this is used to mark a map block so that DF scans it for designated jobs like digging + bool ReadDirtyBit(uint32_t blockx, uint32_t blocky, uint32_t blockz, bool &dirtybit); + bool WriteDirtyBit(uint32_t blockx, uint32_t blocky, uint32_t blockz, bool dirtybit); + + /// read/write the block flags + bool ReadBlockFlags(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_blockflags &blockflags); + bool WriteBlockFlags(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_blockflags blockflags); + + /// read region offsets of a block - used for determining layer stone matgloss + bool ReadRegionOffsets(uint32_t blockx, uint32_t blocky, uint32_t blockz, biome_indices40d *buffer); + + /// read aggregated veins of a block + bool ReadVeins(uint32_t blockx, uint32_t blocky, uint32_t blockz, std::vector & veins, std::vector & ices); + + private: + struct Private; + Private *d; + }; +} +#endif \ No newline at end of file diff --git a/dfhack/include/modules/Materials.h b/dfhack/include/modules/Materials.h new file mode 100644 index 000000000..cfbfb9471 --- /dev/null +++ b/dfhack/include/modules/Materials.h @@ -0,0 +1,53 @@ +#ifndef CL_MOD_MATERIALS +#define CL_MOD_MATERIALS +/* +* Creatures +*/ +#include "Export.h" +namespace DFHack +{ + struct APIPrivate; + + struct t_matgloss + { + char id[128]; //the id in the raws + uint8_t fore; // Annoyingly the offset for this differs between types + uint8_t back; + uint8_t bright; + char name[128]; //this is the name displayed ingame + }; + + struct t_matglossPlant + { + char id[128]; //the id in the raws + uint8_t fore; // Annoyingly the offset for this differs between types + uint8_t back; + uint8_t bright; + char name[128]; //this is the name displayed ingame + char drink_name[128]; //the name this item becomes a drink + char food_name[128]; + char extract_name[128]; + }; + + class DFHACK_EXPORT Materials + { + public: + + Materials(DFHack::APIPrivate * _d); + ~Materials(); + + bool ReadInorganicMaterials (std::vector & output); + bool ReadOrganicMaterials (std::vector & output); + + bool ReadWoodMaterials (std::vector & output); + bool ReadPlantMaterials (std::vector & output); + // bool ReadPlantMaterials (std::vector & output); + + // TODO: maybe move to creatures? + bool ReadCreatureTypes (std::vector & output); + private: + APIPrivate* d; + }; +} +#endif + diff --git a/dfhack/include/modules/Position.h b/dfhack/include/modules/Position.h new file mode 100644 index 000000000..1b1168dde --- /dev/null +++ b/dfhack/include/modules/Position.h @@ -0,0 +1,35 @@ +#ifndef CL_MOD_POSITION +#define CL_MOD_POSITION +/* +* View position and size and cursor position +*/ +#include "Export.h" +namespace DFHack +{ + struct APIPrivate; + class DFHACK_EXPORT Position + { + public: + + Position(APIPrivate * d); + ~Position(); + /* + * Cursor and window coords + */ + bool getViewCoords (int32_t &x, int32_t &y, int32_t &z); + bool setViewCoords (const int32_t x, const int32_t y, const int32_t z); + + bool getCursorCoords (int32_t &x, int32_t &y, int32_t &z); + bool setCursorCoords (const int32_t x, const int32_t y, const int32_t z); + + /* + * Window size in tiles + */ + bool getWindowSize(int32_t & width, int32_t & height); + + private: + struct Private; + Private *d; + }; +} +#endif diff --git a/dfhack/modules/Creatures-data.h b/dfhack/modules/Creatures-data.h deleted file mode 100644 index f8630ceb4..000000000 --- a/dfhack/modules/Creatures-data.h +++ /dev/null @@ -1,4 +0,0 @@ -bool creaturesInited; -Creatures2010::creature_offsets creatures; -uint32_t creature_module; -DfVector *p_cre; \ No newline at end of file diff --git a/dfhack/modules/Creatures-proc.h b/dfhack/modules/Creatures-proc.h deleted file mode 100644 index 6e9c3cfce..000000000 --- a/dfhack/modules/Creatures-proc.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Creatures - */ -bool InitReadCreatures( uint32_t & numcreatures ); - -// Read creatures in a box, starting with index. Returns -1 if no more creatures -// found. Call repeatedly do get all creatures in a specified box (uses tile coords) -int32_t ReadCreatureInBox(const int32_t index, t_creature & furball, - const uint16_t x1, const uint16_t y1,const uint16_t z1, - const uint16_t x2, const uint16_t y2,const uint16_t z2); -bool ReadCreature(const int32_t index, t_creature & furball); -/// write labors of a creature (for Dwarf Therapist) -//bool WriteLabors(const uint32_t index, uint8_t labors[NUM_CREATURE_LABORS]); - -void FinishReadCreatures(); \ No newline at end of file diff --git a/dfhack/modules/Creatures.cpp b/dfhack/modules/Creatures.cpp index c8280543b..903de54c6 100644 --- a/dfhack/modules/Creatures.cpp +++ b/dfhack/modules/Creatures.cpp @@ -25,72 +25,120 @@ distribution. #include "DFCommonInternal.h" #include "../private/APIPrivate.h" -#define SHMCREATURESHDR ((Creatures2010::shm_creature_hdr *)d->shm_start) +// we connect to those +#include +#include +#include +#include "modules/Creatures.h" +#include "DFVector.h" +#include "DFMemInfo.h" +#include "DFProcess.h" +#include "DFError.h" +#include "DFTypes.h" + +#define SHMCREATURESHDR ((Creatures2010::shm_creature_hdr *)d->d->shm_start) +#define SHMCMD(num) ((shm_cmd *)d->d->shm_start)[num]->pingpong +#define SHMHDR ((shm_core_hdr *)d->d->shm_start) +#define SHMDATA(type) ((type *)(d->d->shm_start + SHM_HEADER)) using namespace DFHack; -bool API::InitReadCreatures( uint32_t &numcreatures ) +struct Creatures::Private +{ + bool Inited; + bool Started; + Creatures2010::creature_offsets creatures; + uint32_t creature_module; + DfVector *p_cre; + APIPrivate *d; +}; + +Creatures::Creatures(APIPrivate* _d) { - if(!d->InitReadNames()) return false; + d = new Private; + d->d = _d; + d->Inited = false; + d->Started = false; + d->d->InitReadNames(); // throws on error try { - memory_info * minfo = d->offset_descriptor; - Creatures2010::creature_offsets & off = d->creatures; - off.creature_vector = minfo->getAddress ("creature_vector"); - off.creature_pos_offset = minfo->getOffset ("creature_position"); - off.creature_profession_offset = minfo->getOffset ("creature_profession"); - off.creature_race_offset = minfo->getOffset ("creature_race"); - off.creature_flags1_offset = minfo->getOffset ("creature_flags1"); - off.creature_flags2_offset = minfo->getOffset ("creature_flags2"); - off.creature_name_offset = minfo->getOffset ("creature_name"); - off.creature_sex_offset = minfo->getOffset ("creature_sex"); - off.creature_id_offset = minfo->getOffset ("creature_id"); - off.creature_labors_offset = minfo->getOffset ("creature_labors"); - off.creature_happiness_offset = minfo->getOffset ("creature_happiness"); - off.creature_artifact_name_offset = minfo->getOffset("creature_artifact_name"); + memory_info * minfo = d->d->offset_descriptor; + Creatures2010::creature_offsets &creatures = d->creatures; + creatures.creature_vector = minfo->getAddress ("creature_vector"); + creatures.creature_pos_offset = minfo->getOffset ("creature_position"); + creatures.creature_profession_offset = minfo->getOffset ("creature_profession"); + creatures.creature_race_offset = minfo->getOffset ("creature_race"); + creatures.creature_flags1_offset = minfo->getOffset ("creature_flags1"); + creatures.creature_flags2_offset = minfo->getOffset ("creature_flags2"); + creatures.creature_name_offset = minfo->getOffset ("creature_name"); + creatures.creature_sex_offset = minfo->getOffset ("creature_sex"); + creatures.creature_id_offset = minfo->getOffset ("creature_id"); + creatures.creature_labors_offset = minfo->getOffset ("creature_labors"); + creatures.creature_happiness_offset = minfo->getOffset ("creature_happiness"); + creatures.creature_artifact_name_offset = minfo->getOffset("creature_artifact_name"); // name offsets for the creature module - off.name_firstname_offset = minfo->getOffset("name_firstname"); - off.name_nickname_offset = minfo->getOffset("name_nickname"); - off.name_words_offset = minfo->getOffset("name_words"); + creatures.name_firstname_offset = minfo->getOffset("name_firstname"); + creatures.name_nickname_offset = minfo->getOffset("name_nickname"); + creatures.name_words_offset = minfo->getOffset("name_words"); - d->p_cre = new DfVector (d->p, off.creature_vector, 4); - d->creaturesInited = true; - numcreatures = d->p_cre->getSize(); - - // --> SHM initialization (if possible) <-- - g_pProcess->getModuleIndex("Creatures2010",1,d->creature_module); - - if(d->creature_module) + // upload offsets to the SHM + if(g_pProcess->getModuleIndex("Creatures2010",1,d->creature_module)) { // supply the module with offsets so it can work with them - memcpy(SHMDATA(Creatures2010::creature_offsets),&d->creatures,sizeof(Creatures2010::creature_offsets)); + memcpy(SHMDATA(Creatures2010::creature_offsets),&creatures,sizeof(Creatures2010::creature_offsets)); const uint32_t cmd = Creatures2010::CREATURE_INIT + (d->creature_module << 16); g_pProcess->SetAndWait(cmd); } - return true; + d->Inited = true; } catch (Error::MissingMemoryDefinition&) { - d->creaturesInited = false; - numcreatures = 0; + d->Inited = false; throw; } } -bool API::ReadCreature (const int32_t index, t_creature & furball) +Creatures::~Creatures() { - if(!d->creaturesInited) return false; + if(d->Started) + Finish(); +} + +bool Creatures::Start( uint32_t &numcreatures ) +{ + d->p_cre = new DfVector (d->d->p, d->creatures.creature_vector, 4); + d->Started = true; + numcreatures = d->p_cre->getSize(); + return true; +} + +bool Creatures::Finish() +{ + if(d->p_cre) + { + delete d->p_cre; + d->p_cre = 0; + } + d->Started = false; + return true; +} + +bool Creatures::ReadCreature (const int32_t index, t_creature & furball) +{ + if(!d->Started) return false; + // SHM fast path if(d->creature_module) { - // supply the module with offsets so it can work with them SHMCREATURESHDR->index = index; const uint32_t cmd = Creatures2010::CREATURE_AT_INDEX + (d->creature_module << 16); g_pProcess->SetAndWait(cmd); memcpy(&furball,SHMDATA(t_creature),sizeof(t_creature)); - // cerr << "creature read from SHM!" << endl; return true; } + + // non-SHM slow path + // read pointer from vector at position uint32_t temp = * (uint32_t *) d->p_cre->at (index); furball.origin = temp; @@ -101,9 +149,9 @@ bool API::ReadCreature (const int32_t index, t_creature & furball) g_pProcess->readDWord (temp + offs.creature_flags1_offset, furball.flags1.whole); g_pProcess->readDWord (temp + offs.creature_flags2_offset, furball.flags2.whole); // names - d->readName(furball.name,temp + offs.creature_name_offset); + d->d->readName(furball.name,temp + offs.creature_name_offset); //d->readName(furball.squad_name, temp + offs.creature_squad_name_offset); - d->readName(furball.artifact_name, temp + offs.creature_artifact_name_offset); + d->d->readName(furball.artifact_name, temp + offs.creature_artifact_name_offset); // custom profession //fill_char_buf (furball.custom_profession, d->p->readSTLString (temp + offs.creature_custom_profession_offset)); @@ -173,6 +221,56 @@ bool API::ReadCreature (const int32_t index, t_creature & furball) return true; } + +// returns index of creature actually read or -1 if no creature can be found +int32_t Creatures::ReadCreatureInBox (int32_t index, t_creature & furball, + const uint16_t x1, const uint16_t y1, const uint16_t z1, + const uint16_t x2, const uint16_t y2, const uint16_t z2) +{ + if (!d->Started) return -1; + if(d->creature_module) + { + // supply the module with offsets so it can work with them + SHMCREATURESHDR->index = index; + SHMCREATURESHDR->x = x1; + SHMCREATURESHDR->y = y1; + SHMCREATURESHDR->z = z1; + SHMCREATURESHDR->x2 = x2; + SHMCREATURESHDR->y2 = y2; + SHMCREATURESHDR->z2 = z2; + const uint32_t cmd = Creatures2010::CREATURE_FIND_IN_BOX + (d->creature_module << 16); + g_pProcess->SetAndWait(cmd); + if(SHMCREATURESHDR->index != -1) + memcpy(&furball,SHMDATA(void),sizeof(t_creature)); + return SHMCREATURESHDR->index; + } + else + { + uint16_t coords[3]; + uint32_t size = d->p_cre->getSize(); + while (uint32_t(index) < size) + { + // read pointer from vector at position + uint32_t temp = * (uint32_t *) d->p_cre->at (index); + g_pProcess->read (temp + d->creatures.creature_pos_offset, 3 * sizeof (uint16_t), (uint8_t *) &coords); + if (coords[0] >= x1 && coords[0] < x2) + { + if (coords[1] >= y1 && coords[1] < y2) + { + if (coords[2] >= z1 && coords[2] < z2) + { + ReadCreature (index, furball); + return index; + } + } + } + index++; + } + return -1; + } +} + + /* bool API::WriteLabors(const uint32_t index, uint8_t labors[NUM_CREATURE_LABORS]) { @@ -188,14 +286,4 @@ bool API::getCurrentCursorCreature(uint32_t & creature_index) creature_index = g_pProcess->readDWord(d->current_cursor_creature_offset); return true; } -*/ -void API::FinishReadCreatures() -{ - if(d->p_cre) - { - delete d->p_cre; - d->p_cre = 0; - } - d->creaturesInited = false; - //FinishReadNameTables(); -} \ No newline at end of file +*/ \ No newline at end of file diff --git a/dfhack/modules/Gui-data.h b/dfhack/modules/Gui-data.h deleted file mode 100644 index 2c1fcb058..000000000 --- a/dfhack/modules/Gui-data.h +++ /dev/null @@ -1,4 +0,0 @@ -uint32_t pause_state_offset; -uint32_t view_screen_offset; -uint32_t current_cursor_creature_offset; -uint32_t current_menu_state_offset; \ No newline at end of file diff --git a/dfhack/modules/Gui-proc.h b/dfhack/modules/Gui-proc.h deleted file mode 100644 index e964520ba..000000000 --- a/dfhack/modules/Gui-proc.h +++ /dev/null @@ -1,10 +0,0 @@ -/* -* Query the DF's GUI state -*/ -bool InitReadGuiState(); -///true if paused, false if not -bool ReadPauseState(); -/// read the DF menu view state (stock screen, unit screen, other screens -bool ReadViewScreen(t_viewscreen &); -/// read the DF menu state (designation menu ect) -uint32_t ReadMenuState(); \ No newline at end of file diff --git a/dfhack/modules/Gui.cpp b/dfhack/modules/Gui.cpp index d7b814e6e..eb1d774eb 100644 --- a/dfhack/modules/Gui.cpp +++ b/dfhack/modules/Gui.cpp @@ -24,27 +24,72 @@ distribution. #include "DFCommonInternal.h" #include "../private/APIPrivate.h" +#include "modules/Gui.h" +#include "DFProcess.h" +#include "DFMemInfo.h" +#include "DFTypes.h" + using namespace DFHack; -bool API::ReadPauseState() +struct Gui::Private +{ + bool Inited; + bool Started; + uint32_t pause_state_offset; + uint32_t view_screen_offset; + uint32_t current_cursor_creature_offset; + uint32_t current_menu_state_offset; + APIPrivate *d; +}; + +Gui::Gui(APIPrivate * _d) +{ + + d = new Private; + d->d = _d; + d->Inited = d->Started = true; + + memory_info * mem = d->d->offset_descriptor; + d->current_menu_state_offset = mem->getAddress("current_menu_state"); + d->pause_state_offset = mem->getAddress ("pause_state"); + d->view_screen_offset = mem->getAddress ("view_screen"); + d->Inited = d->Started = true; +} + +Gui::~Gui() +{ + delete d; +} + +bool Gui::Start() +{ + return true; +} + +bool Gui::Finish() +{ + return true; +} + +bool Gui::ReadPauseState() { // replace with an exception - if(!d->cursorWindowInited) return false; + if(!d->Inited) return false; uint32_t pauseState = g_pProcess->readDWord (d->pause_state_offset); return pauseState & 1; } -uint32_t API::ReadMenuState() +uint32_t Gui::ReadMenuState() { - if(d->cursorWindowInited) + if(d->Inited) return(g_pProcess->readDWord(d->current_menu_state_offset)); return false; } -bool API::ReadViewScreen (t_viewscreen &screen) +bool Gui::ReadViewScreen (t_viewscreen &screen) { - if (!d->cursorWindowInited) return false; + if (!d->Inited) return false; uint32_t last = g_pProcess->readDWord (d->view_screen_offset); uint32_t screenAddr = g_pProcess->readDWord (last); @@ -55,5 +100,5 @@ bool API::ReadViewScreen (t_viewscreen &screen) screenAddr = g_pProcess->readDWord (nextScreenPtr); nextScreenPtr = g_pProcess->readDWord (nextScreenPtr + 4); } - return d->offset_descriptor->resolveObjectToClassID (last, screen.type); + return d->d->offset_descriptor->resolveObjectToClassID (last, screen.type); } diff --git a/dfhack/modules/Maps-data.h b/dfhack/modules/Maps-data.h deleted file mode 100644 index 9e187eaab..000000000 --- a/dfhack/modules/Maps-data.h +++ /dev/null @@ -1,17 +0,0 @@ -uint32_t * block; -uint32_t x_block_count, y_block_count, z_block_count; -uint32_t regionX, regionY, regionZ; -uint32_t worldSizeX, worldSizeY; - -uint32_t tile_type_offset; -uint32_t designation_offset; -uint32_t block_flags_offset; - -uint32_t veinvector; -uint32_t vein_mineral_vptr; -uint32_t vein_ice_vptr; -uint32_t vein_spatter_vptr; -uint32_t maps_module; - -//uint32_t biome_stuffs; -//vector v_geology[eBiomeCount]; \ No newline at end of file diff --git a/dfhack/modules/Maps-proc.h b/dfhack/modules/Maps-proc.h deleted file mode 100644 index e3db2105e..000000000 --- a/dfhack/modules/Maps-proc.h +++ /dev/null @@ -1,80 +0,0 @@ -// read region surroundings, get their vectors of geolayers so we can do translation (or just hand the translation table to the client) -// returns an array of 9 vectors of indices into stone matgloss -/** - Method for reading the geological surrounding of the currently loaded region. - assign is a reference to an array of nine vectors of unsigned words that are to be filled with the data - array is indexed by the BiomeOffset enum - - I omitted resolving the layer matgloss in this API, because it would - introduce overhead by calling some method for each tile. You have to do it - yourself. First get the stuff from ReadGeology and then for each block get - the RegionOffsets. For each tile get the real region from RegionOffsets and - cross-reference it with the geology stuff (region -- array of vectors, depth -- - vector). I'm thinking about turning that Geology stuff into a - two-dimensional array with static size. - - this is the algorithm for applying matgloss: - void DfMap::applyGeoMatgloss(Block * b) - { - // load layer matgloss - for(int x_b = 0; x_b < BLOCK_SIZE; x_b++) - { - for(int y_b = 0; y_b < BLOCK_SIZE; y_b++) - { - int geolayer = b->designation[x_b][y_b].bits.geolayer_index; - int biome = b->designation[x_b][y_b].bits.biome; - b->material[x_b][y_b].type = Mat_Stone; - b->material[x_b][y_b].index = v_geology[b->RegionOffsets[biome]][geolayer]; - } - } - } - */ -bool ReadGeology( std::vector < std::vector >& assign ); - -/* - * BLOCK DATA - */ -/// allocate and read pointers to map blocks -bool InitMap(); -/// destroy the mapblock cache -bool DestroyMap(); -/// get size of the map in tiles -void getSize(uint32_t& x, uint32_t& y, uint32_t& z); - -/** - * Return false/0 on failure, buffer allocated by client app, 256 items long - */ -bool isValidBlock(uint32_t blockx, uint32_t blocky, uint32_t blockz); -/** - * Get the address of a block or 0 if block is not valid - */ -uint32_t getBlockPtr (uint32_t blockx, uint32_t blocky, uint32_t blockz); - -/// read the whole map block at block coords (see DFTypes.h for the block structure) -bool ReadBlock40d(uint32_t blockx, uint32_t blocky, uint32_t blockz, mapblock40d * buffer); - -/// read/write block tile types -bool ReadTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, tiletypes40d *buffer); -bool WriteTileTypes(uint32_t blockx, uint32_t blocky, uint32_t blockz, tiletypes40d *buffer); - -/// read/write block designations -bool ReadDesignations(uint32_t blockx, uint32_t blocky, uint32_t blockz, designations40d *buffer); -bool WriteDesignations (uint32_t blockx, uint32_t blocky, uint32_t blockz, designations40d *buffer); - -/// read/write block occupancies -bool ReadOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, occupancies40d *buffer); -bool WriteOccupancy(uint32_t blockx, uint32_t blocky, uint32_t blockz, occupancies40d *buffer); - -/// read/write the block dirty bit - this is used to mark a map block so that DF scans it for designated jobs like digging -bool ReadDirtyBit(uint32_t blockx, uint32_t blocky, uint32_t blockz, bool &dirtybit); -bool WriteDirtyBit(uint32_t blockx, uint32_t blocky, uint32_t blockz, bool dirtybit); - -/// read/write the block flags -bool ReadBlockFlags(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_blockflags &blockflags); -bool WriteBlockFlags(uint32_t blockx, uint32_t blocky, uint32_t blockz, t_blockflags blockflags); - -/// read region offsets of a block - used for determining layer stone matgloss -bool ReadRegionOffsets(uint32_t blockx, uint32_t blocky, uint32_t blockz, biome_indices40d *buffer); - -/// read aggregated veins of a block -bool ReadVeins(uint32_t blockx, uint32_t blocky, uint32_t blockz, std::vector & veins, std::vector & ices); \ No newline at end of file diff --git a/dfhack/modules/Maps.cpp b/dfhack/modules/Maps.cpp index d24999ff8..1f4b48c29 100644 --- a/dfhack/modules/Maps.cpp +++ b/dfhack/modules/Maps.cpp @@ -23,76 +23,108 @@ distribution. */ #include "DFCommonInternal.h" +#include +#include +#include #include "../private/APIPrivate.h" +#include "modules/Maps.h" +#include "DFError.h" +#include "DFMemInfo.h" +#include "DFProcess.h" +#include "DFVector.h" -#define SHMMAPSHDR ((Maps::shm_maps_hdr *)d->shm_start) +#define SHMMAPSHDR ((Server::Maps::shm_maps_hdr *)d->d->shm_start) +#define SHMCMD(num) ((shm_cmd *)d->d->shm_start)[num]->pingpong +#define SHMHDR ((shm_core_hdr *)d->d->shm_start) +#define SHMDATA(type) ((type *)(d->d->shm_start + SHM_HEADER)) using namespace DFHack; -/*-----------------------------------* - * Init the mapblock pointer array * - *-----------------------------------*/ -bool API::InitMap() +struct Maps::Private { - uint32_t map_offset = d->offset_descriptor->getAddress ("map_data"); - uint32_t x_count_offset = d->offset_descriptor->getAddress ("x_count_block"); - uint32_t y_count_offset = d->offset_descriptor->getAddress ("y_count_block"); - uint32_t z_count_offset = d->offset_descriptor->getAddress ("z_count_block"); + uint32_t * block; + uint32_t x_block_count, y_block_count, z_block_count; + uint32_t regionX, regionY, regionZ; + uint32_t worldSizeX, worldSizeY; + uint32_t maps_module; + Server::Maps::maps_offsets offsets; + + APIPrivate *d; + bool Inited; + bool Started; + //uint32_t biome_stuffs; + //vector v_geology[eBiomeCount]; +}; + +Maps::Maps(APIPrivate* _d) +{ + d = new Private; + d->d = _d; + d->Inited = d->Started = false; + + DFHack::memory_info * mem = d->d->offset_descriptor; + Server::Maps::maps_offsets &off = d->offsets; + // get the offsets once here - d->tile_type_offset = d->offset_descriptor->getOffset ("type"); - d->designation_offset = d->offset_descriptor->getOffset ("designation"); - //d->occupancy_offset = d->offset_descriptor->getOffset ("occupancy"); + off.map_offset = mem->getAddress ("map_data"); + off.x_count_offset = mem->getAddress ("x_count_block"); + off.y_count_offset = mem->getAddress ("y_count_block"); + off.z_count_offset = mem->getAddress ("z_count_block"); + off.tile_type_offset = mem->getOffset ("type"); + off.designation_offset = mem->getOffset ("designation"); //d->biome_stuffs = d->offset_descriptor->getOffset ("biome_stuffs"); - - d->veinvector = d->offset_descriptor->getOffset ("v_vein"); + off.veinvector = mem->getOffset ("v_vein"); // these can fail and will be found when looking at the actual veins later // basically a cache - d->vein_ice_vptr = 0; - d->offset_descriptor->resolveClassnameToVPtr("block_square_event_frozen_liquid", d->vein_ice_vptr); - d->vein_mineral_vptr = 0; - d->offset_descriptor->resolveClassnameToVPtr("block_square_event_mineral",d->vein_mineral_vptr); - - /* - * --> SHM initialization (if possible) <-- - */ - /* - g_pProcess->getModuleIndex("Maps2010",1,d->maps_module); - - if(d->maps_module) + off.vein_ice_vptr = 0; + mem->resolveClassnameToVPtr("block_square_event_frozen_liquid", off.vein_ice_vptr); + off.vein_mineral_vptr = 0; + mem->resolveClassnameToVPtr("block_square_event_mineral",off.vein_mineral_vptr); + + // upload offsets to SHM server if possible + d->maps_module = 0; + if(g_pProcess->getModuleIndex("Maps2010",1,d->maps_module)) { // supply the module with offsets so it can work with them - Maps::maps_offsets *off = SHMDATA(Maps::maps_offsets); - off->designation_offset = d->designation_offset; - off->map_offset = map_offset; - off->tile_type_offset = d->tile_type_offset; - off->vein_ice_vptr = d->vein_ice_vptr; // FIXME: not necessarily true, the shm server side needs a class lookup >_< - off->vein_mineral_vptr = d->vein_mineral_vptr; // FIXME: not necessarily true, the shm server side needs a class lookup >_< - off->veinvector = d->veinvector; - off->x_count_offset = x_count_offset; - off->y_count_offset = y_count_offset; - off->z_count_offset = z_count_offset; + Server::Maps::maps_offsets *off2 = SHMDATA(Server::Maps::maps_offsets); + memcpy(off2, &(d->offsets), sizeof(Server::Maps::maps_offsets)); full_barrier - const uint32_t cmd = Maps::MAP_INIT + (d->maps_module << 16); + const uint32_t cmd = Server::Maps::MAP_INIT + (d->maps_module << 16); g_pProcess->SetAndWait(cmd); - //cerr << "Map acceleration enabled!" << endl; } - */ + d->Inited = true; +} + +Maps::~Maps() +{ + if(d->Started) + Finish(); +} + +/*-----------------------------------* + * Init the mapblock pointer array * + *-----------------------------------*/ +bool Maps::Start() +{ + if(!d->Inited) + return false; + if(d->Started) + Finish(); + Server::Maps::maps_offsets &off = d->offsets; // get the map pointer - uint32_t x_array_loc = g_pProcess->readDWord (map_offset); + uint32_t x_array_loc = g_pProcess->readDWord (off.map_offset); if (!x_array_loc) { return false; - // FIXME: only throw this due to programmer error, in the other map functions - //throw Error::NoMapLoaded(); } // get the size uint32_t mx, my, mz; - mx = d->x_block_count = g_pProcess->readDWord (x_count_offset); - my = d->y_block_count = g_pProcess->readDWord (y_count_offset); - mz = d->z_block_count = g_pProcess->readDWord (z_count_offset); + mx = d->x_block_count = g_pProcess->readDWord (off.x_count_offset); + my = d->y_block_count = g_pProcess->readDWord (off.y_count_offset); + mz = d->z_block_count = g_pProcess->readDWord (off.z_count_offset); // test for wrong map dimensions if (mx == 0 || mx > 48 || my == 0 || my > 48 || mz == 0) @@ -125,7 +157,7 @@ bool API::InitMap() return true; } -bool API::DestroyMap() +bool Maps::Finish() { if (d->block != NULL) { @@ -135,28 +167,28 @@ bool API::DestroyMap() return true; } -bool API::isValidBlock (uint32_t x, uint32_t y, uint32_t z) +bool Maps::isValidBlock (uint32_t x, uint32_t y, uint32_t z) { if ( x >= d->x_block_count || y >= d->y_block_count || z >= d->z_block_count) return false; return d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z] != 0; } -uint32_t API::getBlockPtr (uint32_t x, uint32_t y, uint32_t z) +uint32_t Maps::getBlockPtr (uint32_t x, uint32_t y, uint32_t z) { if ( x >= d->x_block_count || y >= d->y_block_count || z >= d->z_block_count) return 0; return d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; } -bool API::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer) +bool Maps::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer) { - if(d->shm_start && d->maps_module) // ACCELERATE! + if(d->d->shm_start && d->maps_module) // ACCELERATE! { SHMMAPSHDR->x = x; SHMMAPSHDR->y = y; SHMMAPSHDR->z = z; - volatile uint32_t cmd = Maps::MAP_READ_BLOCK_BY_COORDS + (d->maps_module << 16); + volatile uint32_t cmd = Server::Maps::MAP_READ_BLOCK_BY_COORDS + (d->maps_module << 16); if(!g_pProcess->SetAndWait(cmd)) return false; memcpy(buffer,SHMDATA(mapblock40d),sizeof(mapblock40d)); @@ -167,7 +199,7 @@ bool API::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer) uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->read (addr + d->tile_type_offset, sizeof (buffer->tiletypes), (uint8_t *) buffer->tiletypes); + g_pProcess->read (addr + d->offsets.tile_type_offset, sizeof (buffer->tiletypes), (uint8_t *) buffer->tiletypes); buffer->origin = addr; uint32_t addr_of_struct = g_pProcess->readDWord(addr); buffer->blockflags.whole = g_pProcess->readDWord(addr_of_struct); @@ -179,18 +211,18 @@ bool API::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer) // 256 * sizeof(uint16_t) -bool API::ReadTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer) +bool Maps::ReadTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->read (addr + d->tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); + g_pProcess->read (addr + d->offsets.tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); return true; } return false; } -bool API::ReadDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool &dirtybit) +bool Maps::ReadDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool &dirtybit) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if(addr) @@ -202,7 +234,7 @@ bool API::ReadDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool &dirtybit) return false; } -bool API::WriteDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool dirtybit) +bool Maps::WriteDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool dirtybit) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) @@ -218,7 +250,7 @@ bool API::WriteDirtyBit(uint32_t x, uint32_t y, uint32_t z, bool dirtybit) } /// read/write the block flags -bool API::ReadBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags &blockflags) +bool Maps::ReadBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags &blockflags) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if(addr) @@ -229,7 +261,7 @@ bool API::ReadBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags &block } return false; } -bool API::WriteBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags blockflags) +bool Maps::WriteBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags blockflags) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) @@ -241,24 +273,24 @@ bool API::WriteBlockFlags(uint32_t x, uint32_t y, uint32_t z, t_blockflags block return false; } -bool API::ReadDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer) +bool Maps::ReadDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->read (addr + d->designation_offset, sizeof (designations40d), (uint8_t *) buffer); + g_pProcess->read (addr + d->offsets.designation_offset, sizeof (designations40d), (uint8_t *) buffer); return true; } return false; } // 256 * sizeof(uint16_t) -bool API::WriteTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer) +bool Maps::WriteTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buffer) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->write (addr + d->tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); + g_pProcess->write (addr + d->offsets.tile_type_offset, sizeof (tiletypes40d), (uint8_t *) buffer); return true; } return false; @@ -266,12 +298,12 @@ bool API::WriteTileTypes (uint32_t x, uint32_t y, uint32_t z, tiletypes40d *buff // 256 * sizeof(uint32_t) -bool API::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer) +bool Maps::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d *buffer) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->write (addr + d->designation_offset, sizeof (designations40d), (uint8_t *) buffer); + g_pProcess->write (addr + d->offsets.designation_offset, sizeof (designations40d), (uint8_t *) buffer); return true; } return false; @@ -281,7 +313,7 @@ bool API::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40d //16 of them? IDK... there's probably just 7. Reading more doesn't cause errors as it's an array nested inside a block // 16 * sizeof(uint8_t) /* -bool API::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40d *buffer) +bool Maps::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40d *buffer) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) @@ -294,16 +326,17 @@ bool API::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40 */ // veins of a block, expects empty vein vectors -bool API::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector & veins, vector & ices) +bool Maps::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector & veins, vector & ices) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; veins.clear(); ices.clear(); - if (addr && d->veinvector) + Server::Maps::maps_offsets &off = d->offsets; + if (addr && off.veinvector) { // veins are stored as a vector of pointers to veins /*pointer is 4 bytes! we work with a 32bit program here, no matter what architecture we compile khazad for*/ - DfVector p_veins (d->p, addr + d->veinvector, 4); + DfVector p_veins (d->d->p, addr + off.veinvector, 4); uint32_t size = p_veins.getSize(); veins.reserve (size); @@ -317,7 +350,7 @@ bool API::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector & veins, uint32_t temp = * (uint32_t *) p_veins[i]; uint32_t type = g_pProcess->readDWord(temp); try_again: - if(type == d->vein_mineral_vptr) + if(type == off.vein_mineral_vptr) { // read the vein data (dereference pointer) g_pProcess->read (temp, sizeof(t_vein), (uint8_t *) &v); @@ -325,7 +358,7 @@ try_again: // store it in the vector veins.push_back (v); } - else if(type == d->vein_ice_vptr) + else if(type == off.vein_ice_vptr) { // read the ice vein data (dereference pointer) g_pProcess->read (temp, sizeof(t_frozenliquidvein), (uint8_t *) &fv); @@ -334,12 +367,12 @@ try_again: } else if(g_pProcess->readClassName(type) == "block_square_event_frozen_liquid") { - d->vein_ice_vptr = type; + off.vein_ice_vptr = type; goto try_again; } else if(g_pProcess->readClassName(type) == "block_square_event_mineral") { - d->vein_mineral_vptr = type; + off.vein_mineral_vptr = type; goto try_again; } } @@ -350,7 +383,7 @@ try_again: // getter for map size -void API::getSize (uint32_t& x, uint32_t& y, uint32_t& z) +void Maps::getSize (uint32_t& x, uint32_t& y, uint32_t& z) { x = d->x_block_count; y = d->y_block_count; diff --git a/dfhack/modules/Materials-data.h b/dfhack/modules/Materials-data.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/dfhack/modules/Materials-proc.h b/dfhack/modules/Materials-proc.h deleted file mode 100644 index bc21262ca..000000000 --- a/dfhack/modules/Materials-proc.h +++ /dev/null @@ -1,13 +0,0 @@ -/* -* Matgloss. next four methods look very similar. I could use two and move the processing one level up... -* I'll keep it like this, even with the code duplication as it will hopefully get more features and separate data types later. -* Yay for nebulous plans for a rock survey tool that tracks how much of which metal could be smelted from available resorces -*/ -bool ReadInorganicMaterials (std::vector & output); -bool ReadOrganicMaterials (std::vector & output); - -bool ReadWoodMaterials (std::vector & output); -bool ReadPlantMaterials (std::vector & output); -// bool ReadPlantMaterials (std::vector & output); - -bool ReadCreatureTypes (std::vector & output); \ No newline at end of file diff --git a/dfhack/modules/Materials.cpp b/dfhack/modules/Materials.cpp index b3ee08f2a..265f5074c 100644 --- a/dfhack/modules/Materials.cpp +++ b/dfhack/modules/Materials.cpp @@ -24,7 +24,18 @@ distribution. #include "DFCommonInternal.h" #include "../private/APIPrivate.h" +#include "modules/Materials.h" +#include "DFVector.h" +#include "DFMemInfo.h" +#include "DFProcess.h" + using namespace DFHack; + +Materials::Materials(APIPrivate * d_) +{ + d = d_; +} +Materials::~Materials(){} /* { LABEL_53: @@ -169,34 +180,34 @@ inline bool ReadNamesOnly(Process* p, uint32_t address, vector & nam return true; } -bool API::ReadInorganicMaterials (vector & inorganic) +bool Materials::ReadInorganicMaterials (vector & inorganic) { return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("mat_inorganics"), inorganic ); } -bool API::ReadOrganicMaterials (vector & organic) +bool Materials::ReadOrganicMaterials (vector & organic) { return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("mat_organics_all"), organic ); } -bool API::ReadWoodMaterials (vector & trees) +bool Materials::ReadWoodMaterials (vector & trees) { return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("mat_organics_trees"), trees ); } -bool API::ReadPlantMaterials (vector & plants) +bool Materials::ReadPlantMaterials (vector & plants) { return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("mat_organics_plants"), plants ); } /* Gives bad results combined with the creature race field! -bool API::ReadCreatureTypes (vector & creatures) +bool Materials::ReadCreatureTypes (vector & creatures) { return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("mat_creature_types"), creatures ); return true; } */ -bool API::ReadCreatureTypes (vector & creatures) +bool Materials::ReadCreatureTypes (vector & creatures) { return ReadNamesOnly(d->p, d->offset_descriptor->getAddress ("creature_type_vector"), creatures ); return true; diff --git a/dfhack/modules/Position-data.h b/dfhack/modules/Position-data.h deleted file mode 100644 index f781e332e..000000000 --- a/dfhack/modules/Position-data.h +++ /dev/null @@ -1,6 +0,0 @@ -uint32_t window_x_offset; -uint32_t window_y_offset; -uint32_t window_z_offset; -uint32_t cursor_xyz_offset; -uint32_t window_dims_offset; -bool cursorWindowInited; \ No newline at end of file diff --git a/dfhack/modules/Position-proc.h b/dfhack/modules/Position-proc.h deleted file mode 100644 index 9ef54b5a2..000000000 --- a/dfhack/modules/Position-proc.h +++ /dev/null @@ -1,17 +0,0 @@ -/* -* Cursor and window coords -*/ -bool InitViewAndCursor(); -bool getViewCoords (int32_t &x, int32_t &y, int32_t &z); -bool setViewCoords (const int32_t x, const int32_t y, const int32_t z); - -bool getCursorCoords (int32_t &x, int32_t &y, int32_t &z); -bool setCursorCoords (const int32_t x, const int32_t y, const int32_t z); - -/* -* Window size in tiles -*/ -bool InitViewSize(); -bool getWindowSize(int32_t & width, int32_t & height); -/// get the creature vector index of the creature currently under DF' cursor -bool getCurrentCursorCreature (uint32_t & creature_index); diff --git a/dfhack/modules/Position.cpp b/dfhack/modules/Position.cpp index 679d1b10c..849f1013e 100644 --- a/dfhack/modules/Position.cpp +++ b/dfhack/modules/Position.cpp @@ -24,24 +24,54 @@ distribution. #include "DFCommonInternal.h" #include "../private/APIPrivate.h" +#include "modules/Position.h" +#include "DFMemInfo.h" +#include "DFProcess.h" using namespace DFHack; -bool API::InitViewAndCursor() + + + +struct Position::Private +{ + uint32_t window_x_offset; + uint32_t window_y_offset; + uint32_t window_z_offset; + uint32_t cursor_xyz_offset; + uint32_t window_dims_offset; + + APIPrivate *d; + bool Inited; + bool Started; + //uint32_t biome_stuffs; + //vector v_geology[eBiomeCount]; +}; + +Position::Position(APIPrivate * d_) +{ + d = new Private; + d->d = d_; + d->Inited = d->Started = false; + memory_info * mem = d->d->offset_descriptor; + d->window_x_offset = mem->getAddress ("window_x"); + d->window_y_offset = mem->getAddress ("window_y"); + d->window_z_offset = mem->getAddress ("window_z"); + d->cursor_xyz_offset = mem->getAddress ("cursor_xyz"); + d->window_dims_offset = mem->getAddress ("window_dims"); + d->Inited = d->Started = true; +} + +Position::~Position() +{ + delete d; +} +/* +bool Position::InitViewAndCursor() { try { - d->window_x_offset = d->offset_descriptor->getAddress ("window_x"); - d->window_y_offset = d->offset_descriptor->getAddress ("window_y"); - d->window_z_offset = d->offset_descriptor->getAddress ("window_z"); - d->cursor_xyz_offset = d->offset_descriptor->getAddress ("cursor_xyz"); - d->current_cursor_creature_offset = d->offset_descriptor->getAddress ("current_cursor_creature"); - d->window_dims_offset = d->offset_descriptor->getAddress ("window_dims"); - d->current_menu_state_offset = d->offset_descriptor->getAddress("current_menu_state"); - d->pause_state_offset = d->offset_descriptor->getAddress ("pause_state"); - d->view_screen_offset = d->offset_descriptor->getAddress ("view_screen"); - - d->cursorWindowInited = true; + d->Inited = true; return true; } catch (Error::MissingMemoryDefinition&) @@ -50,10 +80,10 @@ bool API::InitViewAndCursor() throw; } } - -bool API::getViewCoords (int32_t &x, int32_t &y, int32_t &z) +*/ +bool Position::getViewCoords (int32_t &x, int32_t &y, int32_t &z) { - if (!d->cursorWindowInited) return false; + if (!d->Inited) return false; g_pProcess->readDWord (d->window_x_offset, (uint32_t &) x); g_pProcess->readDWord (d->window_y_offset, (uint32_t &) y); g_pProcess->readDWord (d->window_z_offset, (uint32_t &) z); @@ -61,18 +91,18 @@ bool API::getViewCoords (int32_t &x, int32_t &y, int32_t &z) } //FIXME: confine writing of coords to map bounds? -bool API::setViewCoords (const int32_t x, const int32_t y, const int32_t z) +bool Position::setViewCoords (const int32_t x, const int32_t y, const int32_t z) { - if (!d->cursorWindowInited) return false; + if (!d->Inited) return false; g_pProcess->writeDWord (d->window_x_offset, (uint32_t) x); g_pProcess->writeDWord (d->window_y_offset, (uint32_t) y); g_pProcess->writeDWord (d->window_z_offset, (uint32_t) z); return true; } -bool API::getCursorCoords (int32_t &x, int32_t &y, int32_t &z) +bool Position::getCursorCoords (int32_t &x, int32_t &y, int32_t &z) { - if(!d->cursorWindowInited) return false; + if(!d->Inited) return false; int32_t coords[3]; g_pProcess->read (d->cursor_xyz_offset, 3*sizeof (int32_t), (uint8_t *) coords); x = coords[0]; @@ -83,17 +113,17 @@ bool API::getCursorCoords (int32_t &x, int32_t &y, int32_t &z) } //FIXME: confine writing of coords to map bounds? -bool API::setCursorCoords (const int32_t x, const int32_t y, const int32_t z) +bool Position::setCursorCoords (const int32_t x, const int32_t y, const int32_t z) { - if (!d->cursorWindowInited) return false; + if (!d->Inited) return false; int32_t coords[3] = {x, y, z}; g_pProcess->write (d->cursor_xyz_offset, 3*sizeof (int32_t), (uint8_t *) coords); return true; } -bool API::getWindowSize (int32_t &width, int32_t &height) +bool Position::getWindowSize (int32_t &width, int32_t &height) { - if(! d->cursorWindowInited) return false; + if(!d->Inited) return false; int32_t coords[2]; g_pProcess->read (d->window_dims_offset, 2*sizeof (int32_t), (uint8_t *) coords); diff --git a/dfhack/private/APIPrivate.h b/dfhack/private/APIPrivate.h index 930691ad0..6a2af8d7e 100644 --- a/dfhack/private/APIPrivate.h +++ b/dfhack/private/APIPrivate.h @@ -29,86 +29,85 @@ distribution. #ifndef APIPRIVATE_H_INCLUDED #define APIPRIVATE_H_INCLUDED -// we connect to those -#include -#include -#include -// #include -#include - -#define SHMCMD(num) ((shm_cmd *)d->shm_start)[num]->pingpong -#define SHMHDR ((shm_core_hdr *)d->shm_start) -#define SHMDATA(type) ((type *)(d->shm_start + SHM_HEADER)) - namespace DFHack { - -class APIPrivate -{ -public: - APIPrivate(); - void readName(t_name & name, uint32_t address); - // get the name offsets - bool InitReadNames(); - - #include "../modules/Creatures-data.h" - #include "../modules/Maps-data.h" - #include "../modules/Position-data.h" - #include "../modules/Gui-data.h" - #include "../modules/Materials-data.h" - - uint32_t name_firstname_offset; - uint32_t name_nickname_offset; - uint32_t name_words_offset; - - ProcessEnumerator* pm; - Process* p; - char * shm_start; - memory_info* offset_descriptor; - string xml; - - /* - uint32_t item_material_offset; - - uint32_t note_foreground_offset; - uint32_t note_background_offset; - uint32_t note_name_offset; - uint32_t note_xyz_offset; - uint32_t hotkey_start; - uint32_t hotkey_mode_offset; - uint32_t hotkey_xyz_offset; - uint32_t hotkey_size; - - uint32_t settlement_name_offset; - uint32_t settlement_world_xy_offset; - uint32_t settlement_local_xy_offset; - - uint32_t dwarf_lang_table_offset; - - bool constructionsInited; - bool buildingsInited; - bool effectsInited; - bool vegetationInited; - - - bool itemsInited; - bool notesInited; - bool namesInited; - bool hotkeyInited; - bool settlementsInited; - bool nameTablesInited; - - uint32_t tree_offset; - - DfVector *p_cons; - DfVector *p_bld; - DfVector *p_effect; - DfVector *p_veg; - DfVector *p_itm; - DfVector *p_notes; - DfVector *p_settlements; - DfVector *p_current_settlement; - */ -}; + class Materials; + class Gui; + class Position; + class Maps; + class Creatures; + class ProcessEnumerator; + class Process; + class memory_info; + struct t_name; + class APIPrivate + { + public: + APIPrivate(); + ~APIPrivate(); + void readName(t_name & name, uint32_t address); + // get the name offsets + bool InitReadNames(); + + uint32_t name_firstname_offset; + uint32_t name_nickname_offset; + uint32_t name_words_offset; + + ProcessEnumerator* pm; + Process* p; + char * shm_start; + memory_info* offset_descriptor; + string xml; + + // Modules + Creatures * creatures; + Maps * maps; + Position * position; + Gui * gui; + Materials * materials; + + /* + uint32_t item_material_offset; + + uint32_t note_foreground_offset; + uint32_t note_background_offset; + uint32_t note_name_offset; + uint32_t note_xyz_offset; + uint32_t hotkey_start; + uint32_t hotkey_mode_offset; + uint32_t hotkey_xyz_offset; + uint32_t hotkey_size; + + uint32_t settlement_name_offset; + uint32_t settlement_world_xy_offset; + uint32_t settlement_local_xy_offset; + + uint32_t dwarf_lang_table_offset; + + bool constructionsInited; + bool buildingsInited; + bool effectsInited; + bool vegetationInited; + + + bool itemsInited; + bool notesInited; + bool namesInited; + bool hotkeyInited; + bool settlementsInited; + bool nameTablesInited; + + uint32_t tree_offset; + + DfVector *p_cons; + DfVector *p_bld; + DfVector *p_effect; + DfVector *p_veg; + DfVector *p_itm; + DfVector *p_notes; + DfVector *p_settlements; + DfVector *p_current_settlement; + */ + }; } #endif \ No newline at end of file diff --git a/dfhack/shm/mod-creature2010.h b/dfhack/shm/mod-creature2010.h index 620d21f9d..24795f889 100644 --- a/dfhack/shm/mod-creature2010.h +++ b/dfhack/shm/mod-creature2010.h @@ -77,7 +77,7 @@ enum CREATURE_COMMAND CREATURE_INIT = 0, // initialization CREATURE_FIND_IN_BOX, CREATURE_AT_INDEX, - NUM_CREATURE_CMDS, + NUM_CREATURE_CMDS }; DFPP_module Init(void); diff --git a/dfhack/shm/mod-creature40d.h b/dfhack/shm/mod-creature40d.h index e2cb20567..8c7f84f54 100644 --- a/dfhack/shm/mod-creature40d.h +++ b/dfhack/shm/mod-creature40d.h @@ -27,8 +27,10 @@ distribution. namespace DFHack { - namespace Creatures + namespace Server { + namespace Creatures + { #define CREATURES40D_VERSION 1 typedef struct @@ -100,6 +102,7 @@ enum CREATURE_COMMAND }; DFPP_module Init(void); + } } } diff --git a/dfhack/shm/mod-maps.h b/dfhack/shm/mod-maps.h index eba3a95d4..8023be8bf 100644 --- a/dfhack/shm/mod-maps.h +++ b/dfhack/shm/mod-maps.h @@ -30,8 +30,10 @@ distribution. namespace DFHack { - namespace Maps + namespace Server { + namespace Maps + { #define MAPS_VERSION 3 typedef struct @@ -95,10 +97,10 @@ enum MAPS_COMMAND MAP_READ_BLOCKS_3D, // read blocks between two coords (volumetric) MAP_READ_ALL_BLOCKS, // read the entire map MAP_REVEAL, // reveal the whole map - NUM_MAPS_CMDS, + NUM_MAPS_CMDS }; DFPP_module Init(void); - + } } } diff --git a/dfhack/shm/shms.h b/dfhack/shm/shms.h index ba818d434..ce7913383 100644 --- a/dfhack/shm/shms.h +++ b/dfhack/shm/shms.h @@ -41,7 +41,7 @@ enum DFPP_CmdType { CANCELLATION, // we should jump out of the Act() CLIENT_WAIT, // we are waiting for the client - FUNCTION, // we call a function as a result of the command + FUNCTION // we call a function as a result of the command }; struct DFPP_command diff --git a/examples/creaturedump.cpp b/examples/creaturedump.cpp index f9ec1cf18..60cb343be 100644 --- a/examples/creaturedump.cpp +++ b/examples/creaturedump.cpp @@ -10,6 +10,8 @@ using namespace std; #include #include #include +#include +#include template void print_bits ( T val, std::ostream& out ) @@ -321,8 +323,11 @@ int main (void) return 1; } + DFHack::Creatures * Creatures = DF.getCreatures(); + DFHack::Materials * Materials = DF.getMaterials(); + uint32_t numCreatures; - if(!DF.InitReadCreatures(numCreatures)) + if(!Creatures->Start(numCreatures)) { cerr << "Can't get creatures" << endl; #ifndef LINUX_BUILD @@ -348,7 +353,7 @@ int main (void) */ mem = DF.getMemoryInfo(); // get stone matgloss mapping - if(!DF.ReadCreatureTypes(creaturestypes)) + if(!Materials->ReadCreatureTypes(creaturestypes)) { cerr << "Can't get the creature types." << endl; return 1; @@ -364,7 +369,7 @@ int main (void) for(uint32_t i = 0; i < numCreatures; i++) { DFHack::t_creature temp; - DF.ReadCreature(i,temp); + Creatures->ReadCreature(i,temp); //if(string(creaturestypes[temp.type].id) == "DWARF") { cout << "index " << i << " "; @@ -380,7 +385,7 @@ int main (void) DF.ReadCreature(currentIdx, currentCreature); printCreature(DF,currentCreature); */ - DF.FinishReadCreatures(); + Creatures->Finish(); DF.Detach(); #ifndef LINUX_BUILD cout << "Done. Press any key to continue" << endl; diff --git a/examples/expbench.cpp b/examples/expbench.cpp index 7d674098b..579f7ab4a 100644 --- a/examples/expbench.cpp +++ b/examples/expbench.cpp @@ -11,6 +11,7 @@ using namespace std; #include #include +#include void print_progress (int current, int total) { @@ -48,11 +49,12 @@ int main (int numargs, char** args) uint32_t num_blocks = 0; uint64_t bytes_read = 0; DFHack::mapblock40d Block; - + DFHack::Maps *Maps = 0; DFHack::API DF("Memory.xml"); try { DF.Attach(); + Maps = DF.getMaps(); } catch (exception& e) { @@ -70,25 +72,25 @@ int main (int numargs, char** args) { print_progress (i, iterations); - if(!DF.InitMap()) + if(!Maps->Start()) break; - DF.getSize(x_max,y_max,z_max); + Maps->getSize(x_max,y_max,z_max); for(uint32_t x = 0; x< x_max;x++) { for(uint32_t y = 0; y< y_max;y++) { for(uint32_t z = 0; z< z_max;z++) { - if(DF.isValidBlock(x,y,z)) + if(Maps->isValidBlock(x,y,z)) { - DF.ReadBlock40d(x, y, z, &Block); + Maps->ReadBlock40d(x, y, z, &Block); num_blocks ++; bytes_read += sizeof(DFHack::mapblock40d); } } } } - DF.DestroyMap(); + Maps->Finish(); } DF.Detach(); time(&end); diff --git a/examples/materialtest.cpp b/examples/materialtest.cpp index c5a431cf5..5f5acdda3 100644 --- a/examples/materialtest.cpp +++ b/examples/materialtest.cpp @@ -13,6 +13,7 @@ using namespace std; #include #include #include +#include void DumpObjStr0Vector (const char * name, DFHack::Process *p, uint32_t addr) { @@ -62,6 +63,8 @@ int main (int numargs, const char ** args) DFHack::Process* p = DF.getProcess(); DFHack::memory_info* mem = DF.getMemoryInfo(); + DFHack::Materials *Materials = DF.getMaterials(); + //const vector * names = mem->getClassIDMapping(); /* DumpObjStr0Vector("Material templates",p, mem->getAddress("mat_templates")); @@ -91,7 +94,7 @@ int main (int numargs, const char ** args) cout << "----==== Inorganic ====----" << endl; vector matgloss; - DF.ReadInorganicMaterials (matgloss); + Materials->ReadInorganicMaterials (matgloss); for(int i = 0; i < matgloss.size();i++) { cout << matgloss[i].id << endl; @@ -99,14 +102,14 @@ int main (int numargs, const char ** args) cout << endl << "----==== Organic ====----" << endl; vector organic; - DF.ReadOrganicMaterials (matgloss); + Materials->ReadOrganicMaterials (matgloss); for(int i = 0; i < matgloss.size();i++) { cout << matgloss[i].id << endl; } cout << endl << "----==== Creature types ====----" << endl; vector creature; - DF.ReadCreatureTypes (matgloss); + Materials->ReadCreatureTypes (matgloss); for(int i = 0; i < matgloss.size();i++) { cout << matgloss[i].id << endl; diff --git a/examples/position.cpp b/examples/position.cpp index a2120bac7..4e1787477 100644 --- a/examples/position.cpp +++ b/examples/position.cpp @@ -9,13 +9,16 @@ using namespace std; #include #include +#include int main (void) { DFHack::API DF("Memory.xml"); + DFHack::Position * Position = 0; try { DF.Attach(); + Position = DF.getPosition(); } catch (exception& e) { @@ -25,17 +28,16 @@ int main (void) #endif return 1; } - - if (DF.InitViewAndCursor()) + if (Position) { int32_t x,y,z; int32_t width,height; - if(DF.getViewCoords(x,y,z)) + if(Position->getViewCoords(x,y,z)) cout << "view coords: " << x << "/" << y << "/" << z << endl; - if(DF.getCursorCoords(x,y,z)) + if(Position->getCursorCoords(x,y,z)) cout << "cursor coords: " << x << "/" << y << "/" << z << endl; - if(DF.getWindowSize(width,height)) + if(Position->getWindowSize(width,height)) cout << "window size : " << width << " " << height << endl; } else diff --git a/examples/veinlook.cpp b/examples/veinlook.cpp index 94c4cd20c..b22723fa5 100644 --- a/examples/veinlook.cpp +++ b/examples/veinlook.cpp @@ -8,17 +8,21 @@ #include using namespace std; +#include +#include "fake-curses.h" +#include +#include +#include + #include #include #include #include #include +#include +#include using namespace DFHack; -#include -#include "fake-curses.h" -#include -#include -#include + string error; API * pDF = 0; @@ -308,10 +312,16 @@ main(int argc, char *argv[]) vector veinVector; vector IceVeinVector; + DFHack::Materials * Mats = 0; + DFHack::Maps * Maps = 0; + + DFHack::API DF("Memory.xml"); try { DF.Attach(); + Mats = DF.getMaterials(); + Maps = DF.getMaps(); pDF = &DF; } catch (exception& e) @@ -325,20 +335,20 @@ main(int argc, char *argv[]) Process* p = DF.getProcess(); // init the map - if(!DF.InitMap()) + if(!Maps->Start()) { error = "Can't find a map to look at."; pDF = 0; finish(0); } - DF.getSize(x_max_a,y_max_a,z_max_a); + Maps->getSize(x_max_a,y_max_a,z_max_a); x_max = x_max_a; y_max = y_max_a; z_max = z_max_a; // get stone matgloss mapping - if(!DF.ReadInorganicMaterials(stonetypes)) + if(!Mats->ReadInorganicMaterials(stonetypes)) { error = "Can't read stone types."; pDF = 0; @@ -455,18 +465,18 @@ main(int argc, char *argv[]) mapblock40d * Block = &blocks[i+1][j+1]; - if(DF.isValidBlock(cursorX+i,cursorY+j,cursorZ)) + if(Maps->isValidBlock(cursorX+i,cursorY+j,cursorZ)) { - DF.ReadBlock40d(cursorX+i,cursorY+j,cursorZ, Block); + Maps->ReadBlock40d(cursorX+i,cursorY+j,cursorZ, Block); // extra processing of the block in the middle if(i == 0 && j == 0) { // read veins - DF.ReadVeins(cursorX+i,cursorY+j,cursorZ,veinVector,IceVeinVector); + Maps->ReadVeins(cursorX+i,cursorY+j,cursorZ,veinVector,IceVeinVector); // get pointer to block - blockaddr = DF.getBlockPtr(cursorX+i,cursorY+j,cursorZ); + blockaddr = Maps->getBlockPtr(cursorX+i,cursorY+j,cursorZ); blockaddr2 = Block->origin; // dig all veins and trees @@ -481,7 +491,7 @@ main(int argc, char *argv[]) Block->designation[x][y].bits.dig = designation_default; } } - DF.WriteDesignations(cursorX+i,cursorY+j,cursorZ, &(Block->designation)); + Maps->WriteDesignations(cursorX+i,cursorY+j,cursorZ, &(Block->designation)); } // do a dump of the block data if(dump) @@ -490,12 +500,12 @@ main(int argc, char *argv[]) filenum++; } // read/write dirty bit of the block - DF.ReadDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); - DF.ReadBlockFlags(cursorX+i,cursorY+j,cursorZ,bflags); + Maps->ReadDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); + Maps->ReadBlockFlags(cursorX+i,cursorY+j,cursorZ,bflags); if(digbit) { dirtybit = !dirtybit; - DF.WriteDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); + Maps->WriteDirtyBit(cursorX+i,cursorY+j,cursorZ,dirtybit); } } } diff --git a/tools/prospector.cpp b/tools/prospector.cpp index 631a9864b..eaf9cc275 100644 --- a/tools/prospector.cpp +++ b/tools/prospector.cpp @@ -19,6 +19,8 @@ using namespace std; #include #include #include +#include +#include int main (int argc, const char* argv[]) { @@ -69,8 +71,12 @@ int main (int argc, const char* argv[]) return 1; } + + DFHack::Maps * Maps = DF.getMaps(); + DFHack::Materials * Mats = DF.getMaterials(); + // init the map - if(!DF.InitMap()) + if(!Maps->Start()) { cerr << "Can't init map." << endl; #ifndef LINUX_BUILD @@ -78,10 +84,10 @@ int main (int argc, const char* argv[]) #endif return 1; } - DF.getSize(x_max,y_max,z_max); + Maps->getSize(x_max,y_max,z_max); // get stone matgloss mapping - if(!DF.ReadInorganicMaterials(stonetypes)) + if(!Mats->ReadInorganicMaterials(stonetypes)) { //DF.DestroyMap(); cerr << "Can't get the materials." << endl; @@ -113,16 +119,16 @@ int main (int argc, const char* argv[]) { for(uint32_t z = 0; z< z_max;z++) { - if(!DF.isValidBlock(x,y,z)) + if(!Maps->isValidBlock(x,y,z)) continue; // read data - DF.ReadTileTypes(x,y,z, &tiletypes); - DF.ReadDesignations(x,y,z, &designations); + Maps->ReadTileTypes(x,y,z, &tiletypes); + Maps->ReadDesignations(x,y,z, &designations); memset(tempvein, -1, sizeof(tempvein)); veins.clear(); - DF.ReadVeins(x,y,z,veins,iceveins); + Maps->ReadVeins(x,y,z,veins,iceveins); /* if(showbaselayers) { diff --git a/tools/reveal.cpp b/tools/reveal.cpp index 654252e32..de1304e34 100644 --- a/tools/reveal.cpp +++ b/tools/reveal.cpp @@ -7,6 +7,7 @@ using namespace std; #include #include +#include int main (void) { @@ -27,8 +28,9 @@ int main (void) return 1; } + DFHack::Maps *Maps =DF.getMaps(); // init the map - if(!DF.InitMap()) + if(!Maps->Start()) { cerr << "Can't init map." << endl; #ifndef LINUX_BUILD @@ -37,7 +39,7 @@ int main (void) return 1; } - DF.getSize(x_max,y_max,z_max); + Maps->getSize(x_max,y_max,z_max); // walk the map for(uint32_t x = 0; x< x_max;x++) @@ -46,17 +48,17 @@ int main (void) { for(uint32_t z = 0; z< z_max;z++) { - if(DF.isValidBlock(x,y,z)) + if(Maps->isValidBlock(x,y,z)) { // read block designations - DF.ReadDesignations(x,y,z, &designations); + Maps->ReadDesignations(x,y,z, &designations); // change the hidden flag to 0 for (uint32_t i = 0; i < 16;i++) for (uint32_t j = 0; j < 16;j++) { designations[i][j].bits.hidden = 0; } // write the designations back - DF.WriteDesignations(x,y,z, &designations); + Maps->WriteDesignations(x,y,z, &designations); } } } From f8d1c042d7dd2d223cdc4dde42bdbb75e365a5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 5 Apr 2010 05:29:46 +0200 Subject: [PATCH 28/29] Layer stone/geology --- dfhack/modules/Maps.cpp | 94 +++++++++++++++++++++++++++++-------- dfhack/private/APIPrivate.h | 5 +- output/Memory.xml | 44 +++++++++++++++-- tools/prospector.cpp | 12 ++--- 4 files changed, 125 insertions(+), 30 deletions(-) diff --git a/dfhack/modules/Maps.cpp b/dfhack/modules/Maps.cpp index 1f4b48c29..9bf265f5f 100644 --- a/dfhack/modules/Maps.cpp +++ b/dfhack/modules/Maps.cpp @@ -54,7 +54,7 @@ struct Maps::Private bool Inited; bool Started; //uint32_t biome_stuffs; - //vector v_geology[eBiomeCount]; + vector v_geology[eBiomeCount]; }; Maps::Maps(APIPrivate* _d) @@ -73,7 +73,7 @@ Maps::Maps(APIPrivate* _d) off.z_count_offset = mem->getAddress ("z_count_block"); off.tile_type_offset = mem->getOffset ("type"); off.designation_offset = mem->getOffset ("designation"); - //d->biome_stuffs = d->offset_descriptor->getOffset ("biome_stuffs"); + off.biome_stuffs = mem->getOffset ("biome_stuffs"); off.veinvector = mem->getOffset ("v_vein"); // these can fail and will be found when looking at the actual veins later @@ -312,18 +312,18 @@ bool Maps::WriteDesignations (uint32_t x, uint32_t y, uint32_t z, designations40 // FIXME: this is bad. determine the real size! //16 of them? IDK... there's probably just 7. Reading more doesn't cause errors as it's an array nested inside a block // 16 * sizeof(uint8_t) -/* + bool Maps::ReadRegionOffsets (uint32_t x, uint32_t y, uint32_t z, biome_indices40d *buffer) { uint32_t addr = d->block[x*d->y_block_count*d->z_block_count + y*d->z_block_count + z]; if (addr) { - g_pProcess->read (addr + d->biome_stuffs, sizeof (biome_indices40d), (uint8_t *) buffer); + g_pProcess->read (addr + d->offsets.biome_stuffs, sizeof (biome_indices40d), (uint8_t *) buffer); return true; } return false; } -*/ + // veins of a block, expects empty vein vectors bool Maps::ReadVeins(uint32_t x, uint32_t y, uint32_t z, vector & veins, vector & ices) @@ -391,22 +391,79 @@ void Maps::getSize (uint32_t& x, uint32_t& y, uint32_t& z) } /* +__int16 __userpurge GetGeologicalRegion(__int16 block_X, int X, __int16 block_Y, int block_addr, int Y) +{ + char bio_off; // al@1 + int tile_designation; // ecx@1 + __int64 corrected_x; // qax@1 + __int64 corrected_y; // qax@1 + int difY; // eax@9 + int difX; // edx@9 + signed __int64 bio_off_2; // qax@9 + signed __int64 bio_off_2_; // qtt@9 + __int16 result; // ax@23 + + corrected_x = reg_off_x + (block_X + (signed int)*(_WORD *)(block_addr + 0x90)) / 48; + *(_WORD *)X = ((BYTE4(corrected_x) & 0xF) + (_DWORD)corrected_x) >> 4; + corrected_y = reg_off_y + (block_Y + (signed int)*(_WORD *)(block_addr + 0x92)) / 48; + *(_WORD *)Y = ((BYTE4(corrected_y) & 0xF) + (_DWORD)corrected_y) >> 4; + tile_designation = *(_DWORD *)(block_addr + 4 * (block_Y + 16 * block_X) + 0x29C); + bio_off = 0; + if ( tile_designation & 0x20000 ) + bio_off = 1; + if ( tile_designation & 0x40000 ) + bio_off |= 2u; + if ( tile_designation & 0x80000 ) + bio_off |= 4u; + if ( tile_designation & 0x100000 ) + bio_off |= 8u; + bio_off_2 = *(_BYTE *)(bio_off + block_addr + 0x1D9C); + bio_off_2_ = bio_off_2; + difY = bio_off_2 / 3; + difX = bio_off_2_ % 3; + if ( !difX ) + --*(_WORD *)X; + if ( difX == 2 ) + ++*(_WORD *)X; + if ( !difY ) + --*(_WORD *)Y; + if ( difY == 2 ) + ++*(_WORD *)Y; + if ( *(_WORD *)X < 0 ) + *(_WORD *)X = 0; + if ( *(_WORD *)Y < 0 ) + *(_WORD *)Y = 0; + if ( *(_WORD *)X >= (_WORD)World_size ) + *(_WORD *)X = World_size - 1; + result = HIWORD(World_size); + if ( *(_WORD *)Y >= HIWORD(World_size) ) + { + result = HIWORD(World_size) - 1; + *(_WORD *)Y = HIWORD(World_size) - 1; + } + return result; +} +*/ + //vector v_geology[eBiomeCount]; -bool API::ReadGeology (vector < vector >& assign) +bool Maps::ReadGeology (vector < vector >& assign) { - memory_info * minfo = d->offset_descriptor; + memory_info * minfo = d->d->offset_descriptor; // get needed addresses and offsets. Now this is what I call crazy. int region_x_offset = minfo->getAddress ("region_x"); int region_y_offset = minfo->getAddress ("region_y"); int region_z_offset = minfo->getAddress ("region_z"); - int world_offset = minfo->getAddress ("world"); - int world_regions_offset = minfo->getOffset ("w_regions_arr"); +/*
0x16AF52C
+
0x16AF574
*/ + int world_regions = minfo->getAddress ("ptr2_region_array"); int region_size = minfo->getHexValue ("region_size"); int region_geo_index_offset = minfo->getOffset ("region_geo_index_off"); - int world_geoblocks_offset = minfo->getOffset ("w_geoblocks"); - int world_size_x = minfo->getOffset ("world_size_x"); - int world_size_y = minfo->getOffset ("world_size_y"); + int world_geoblocks_vector = minfo->getAddress ("geoblock_vector"); + int world_size_x = minfo->getAddress ("world_size_x"); + int world_size_y = minfo->getAddress ("world_size_y"); int geolayer_geoblock_offset = minfo->getOffset ("geolayer_geoblock_offset"); + + int type_inside_geolayer = minfo->getOffset ("type_inside_geolayer"); uint32_t regionX, regionY, regionZ; uint16_t worldSizeX, worldSizeY; @@ -417,14 +474,14 @@ bool API::ReadGeology (vector < vector >& assign) g_pProcess->readDWord (region_z_offset, regionZ); // get world size - g_pProcess->readWord (world_offset + world_size_x, worldSizeX); - g_pProcess->readWord (world_offset + world_size_y, worldSizeY); + g_pProcess->readWord (world_size_x, worldSizeX); + g_pProcess->readWord (world_size_y, worldSizeY); // get pointer to first part of 2d array of regions - uint32_t regions = g_pProcess->readDWord (world_offset + world_regions_offset); + uint32_t regions = g_pProcess->readDWord (world_regions); // read the geoblock vector - DfVector geoblocks (d->p, world_offset + world_geoblocks_offset, 4); + DfVector geoblocks (d->d->p, world_geoblocks_vector, 4); // iterate over 8 surrounding regions + local region for (int i = eNorthWest; i < eBiomeCount; i++) @@ -450,7 +507,7 @@ bool API::ReadGeology (vector < vector >& assign) uint32_t geoblock_off = * (uint32_t *) geoblocks[geoindex]; // get the vector with pointer to layers - DfVector geolayers (d->p, geoblock_off + geolayer_geoblock_offset , 4); // let's hope + DfVector geolayers (d->d->p, geoblock_off + geolayer_geoblock_offset , 4); // let's hope // make sure we don't load crap assert (geolayers.getSize() > 0 && geolayers.getSize() <= 16); @@ -461,7 +518,7 @@ bool API::ReadGeology (vector < vector >& assign) // read pointer to a layer uint32_t geol_offset = * (uint32_t *) geolayers[j]; // read word at pointer + 2, store in our geology vectors - d->v_geology[i].push_back (g_pProcess->readWord (geol_offset + 2)); + d->v_geology[i].push_back (g_pProcess->readWord (geol_offset + type_inside_geolayer)); } } assign.clear(); @@ -473,4 +530,3 @@ bool API::ReadGeology (vector < vector >& assign) } return true; } -*/ \ No newline at end of file diff --git a/dfhack/private/APIPrivate.h b/dfhack/private/APIPrivate.h index 6a2af8d7e..07bd63adc 100644 --- a/dfhack/private/APIPrivate.h +++ b/dfhack/private/APIPrivate.h @@ -45,13 +45,15 @@ namespace DFHack public: APIPrivate(); ~APIPrivate(); + + // names, used by a few other modules. void readName(t_name & name, uint32_t address); // get the name offsets bool InitReadNames(); - uint32_t name_firstname_offset; uint32_t name_nickname_offset; uint32_t name_words_offset; + bool namesInited; ProcessEnumerator* pm; Process* p; @@ -92,7 +94,6 @@ namespace DFHack bool itemsInited; bool notesInited; - bool namesInited; bool hotkeyInited; bool settlementsInited; bool nameTablesInited; diff --git a/output/Memory.xml b/output/Memory.xml index dc0382c20..7c3103c51 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -2899,10 +2899,28 @@ 0x08 0x009A 0x029C + + + 0x1D9C + * map size in blocks * @@ -2919,7 +2937,27 @@
0x016ad750
0x016ad754
0x016ad758
- + + * World size * (WORDs) +
0x016AEDD4
+
0x016AEDD6
+ +
0x16AF52C
+
0x16AF574
+ + + + 0x64 + 0x60 + + 0x4 vector + 0x4 vector + Name struct =========== 0x0 diff --git a/tools/prospector.cpp b/tools/prospector.cpp index eaf9cc275..f944638dc 100644 --- a/tools/prospector.cpp +++ b/tools/prospector.cpp @@ -96,9 +96,9 @@ int main (int argc, const char* argv[]) #endif return 1; } - /* + // get region geology - if(!DF.ReadGeology( layerassign )) + if(!Maps->ReadGeology( layerassign )) { cerr << "Can't get region geology." << endl; #ifndef LINUX_BUILD @@ -106,7 +106,7 @@ int main (int argc, const char* argv[]) #endif return 1; } - */ + int16_t tempvein [16][16]; vector veins; vector iceveins; @@ -129,10 +129,10 @@ int main (int argc, const char* argv[]) memset(tempvein, -1, sizeof(tempvein)); veins.clear(); Maps->ReadVeins(x,y,z,veins,iceveins); - /* + if(showbaselayers) { - DF.ReadRegionOffsets(x,y,z, ®ionoffsets); + Maps->ReadRegionOffsets(x,y,z, ®ionoffsets); // get the layer materials for(uint32_t xx = 0;xx<16;xx++) { @@ -153,7 +153,7 @@ int main (int argc, const char* argv[]) } } } - */ + // for each vein for(int i = 0; i < (int)veins.size();i++) { From ea120a6709029ce24daa113476b3bae045d982ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Mon, 5 Apr 2010 05:53:38 +0200 Subject: [PATCH 29/29] Comments --- dfhack/modules/Maps.cpp | 8 ++++++-- output/Memory.xml | 8 +------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/dfhack/modules/Maps.cpp b/dfhack/modules/Maps.cpp index 9bf265f5f..af468510e 100644 --- a/dfhack/modules/Maps.cpp +++ b/dfhack/modules/Maps.cpp @@ -486,7 +486,7 @@ bool Maps::ReadGeology (vector < vector >& assign) // iterate over 8 surrounding regions + local region for (int i = eNorthWest; i < eBiomeCount; i++) { - // check bounds, fix them if needed + // check against worldmap boundaries, fix if needed int bioRX = regionX / 16 + (i % 3) - 1; if (bioRX < 0) bioRX = 0; if (bioRX >= worldSizeX) bioRX = worldSizeX - 1; @@ -494,6 +494,8 @@ bool Maps::ReadGeology (vector < vector >& assign) if (bioRY < 0) bioRY = 0; if (bioRY >= worldSizeY) bioRY = worldSizeY - 1; + /// regions are a 2d array. consists of pointers to arrays of regions + /// regions are of region_size size // get pointer to column of regions uint32_t geoX; g_pProcess->readDWord (regions + bioRX*4, geoX); @@ -502,15 +504,18 @@ bool Maps::ReadGeology (vector < vector >& assign) uint16_t geoindex; g_pProcess->readWord (geoX + bioRY*region_size + region_geo_index_offset, geoindex); + /// geology blocks are assigned to regions from a vector // get the geoblock from the geoblock vector using the geoindex // read the matgloss pointer from the vector into temp uint32_t geoblock_off = * (uint32_t *) geoblocks[geoindex]; + /// geology blocks have a vector of layer descriptors // get the vector with pointer to layers DfVector geolayers (d->d->p, geoblock_off + geolayer_geoblock_offset , 4); // let's hope // make sure we don't load crap assert (geolayers.getSize() > 0 && geolayers.getSize() <= 16); + /// layer descriptor has a field that determines the type of stone/soil d->v_geology[i].reserve (geolayers.getSize()); // finally, read the layer matgloss for (uint32_t j = 0;j < geolayers.getSize();j++) @@ -523,7 +528,6 @@ bool Maps::ReadGeology (vector < vector >& assign) } assign.clear(); assign.reserve (eBiomeCount); -// // TODO: clean this up for (int i = 0; i < eBiomeCount;i++) { assign.push_back (d->v_geology[i]); diff --git a/output/Memory.xml b/output/Memory.xml index 7c3103c51..f4d5a751d 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -2899,8 +2899,7 @@ 0x08 0x009A 0x029C - - + 0x1D9C - * map size in blocks *
0x016ad738