[X3LU][KC] .obj files editing
Moderators: Moderators for English X Forum, Scripting / Modding Moderators
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
[X3LU][KC] .obj files editing
Hello!
First of all, a disclaimer : this topic was posted mainly to understand the intricacies of modifying .obj files, purely as a learning exercise. We'll assume that the subject is considered a grey-ish area, and that the theoretical methods for accessing and reinserting those are known. Also, please don't reply with requests for mods etc...
Disclaimer #2 : while not being entirely clueless about programming, I've never written compiled production code.
...
Let's start with an example : a while ago, I was trying to add to the 'famous quotes' displayed during the intro and made a list of another 67 of those. Adding those to the proper page # wasn't enough, even when inserted directly into 0001-L044. The conclusion I arrived to is that the total number of quotes (68 in LU) from which one is randomly picked is hard-coded somewhere other than the usual mod-related files. After digging around some more, this value seems to be defined in x3intro.obj (based on a test where adding an empty x3intro.obj crashes the game at the proper time with the error 'File not found : x3intro'). However, changing any/some/all 68d(44h) values to 135d(87h) never loads quotes past the 68th. Furthermore, changing only the most likely candidate (push 68d; call SE_random) has for effect to load the first quote (#0) listed in 0001-L044 but none of the others...
I'm confident that the .obj is properly generated since inserting an unmodified, de-compiled/re-compiled .obj works fine. On a side note, I also tried hex-editing (aka blind-gambling) the only reference to x06x44, assuming the x86 op-codes remain valid. No go.
So, *IF* the aforementioned approach is sound, the question boils down to : what aspect of the loading sequence or KC-related intricacy am I missing here?
Thanks a lot,
-lpa
First of all, a disclaimer : this topic was posted mainly to understand the intricacies of modifying .obj files, purely as a learning exercise. We'll assume that the subject is considered a grey-ish area, and that the theoretical methods for accessing and reinserting those are known. Also, please don't reply with requests for mods etc...
Disclaimer #2 : while not being entirely clueless about programming, I've never written compiled production code.
...
Let's start with an example : a while ago, I was trying to add to the 'famous quotes' displayed during the intro and made a list of another 67 of those. Adding those to the proper page # wasn't enough, even when inserted directly into 0001-L044. The conclusion I arrived to is that the total number of quotes (68 in LU) from which one is randomly picked is hard-coded somewhere other than the usual mod-related files. After digging around some more, this value seems to be defined in x3intro.obj (based on a test where adding an empty x3intro.obj crashes the game at the proper time with the error 'File not found : x3intro'). However, changing any/some/all 68d(44h) values to 135d(87h) never loads quotes past the 68th. Furthermore, changing only the most likely candidate (push 68d; call SE_random) has for effect to load the first quote (#0) listed in 0001-L044 but none of the others...
I'm confident that the .obj is properly generated since inserting an unmodified, de-compiled/re-compiled .obj works fine. On a side note, I also tried hex-editing (aka blind-gambling) the only reference to x06x44, assuming the x86 op-codes remain valid. No go.
So, *IF* the aforementioned approach is sound, the question boils down to : what aspect of the loading sequence or KC-related intricacy am I missing here?
Thanks a lot,
-lpa
Modifying
to
*should* have worked, its very odd that it didnt. Did you place the new file in the <GameRoot>\L directory, and not the <GameRoot>\addon\L directory? The first is valid, the latter isnt.
If you want to see if your new strings are even beeing loaded, consider adding a direct offset instead of changing the random amount:
P.S. This is alot of effort to add some strings the most users never really see anyway I know i always skip the egosoft into logo which skips the quote too haha.
Code: Select all
pushb 68d ; 44h
push 1
callasm SE_Random
push 1
add SP[0],SP[1]
Code: Select all
pushb 135d
push 1
callasm SE_Random
push 1
add SP[0],SP[1]
If you want to see if your new strings are even beeing loaded, consider adding a direct offset instead of changing the random amount:
Code: Select all
pushb 68d ; 44h
push 1
callasm SE_Random
pushb 68d ; <<<Offset
add SP[0],SP[1]
[ external image ]
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
Hi!
Your suggestion about using an offset did confirm that the new quotes get loaded when forcing values > 68d.
I later discovered that using values > 127d to reference quote strings always returns index 0.
So, there can only be 128 quotes in total... Having 135 did allow for discovering that limit, at least. However, I wasn't able to track down where that limit is set, but I didn't put that much effort into it (reading asm SUCKS).
___
Next project : testing x3galedit.obj to see if the 24*20 sector count is indeed limited by hard-coded loops like in x2story.cpp:
I'm guessing it is after looking at the asm, but it'll be really hard to confirm since compiling files larger than x3intro.obj crashes systematically. I've also looked a bit into hex-editing these, but I can't tell for sure if KC's op-codes are consistent with those of x86. Fun stuff
Thanks again,
-lpa
PS: I can't believe you guys in the LU team actually managed to pull it off while dealing with all this asm crap... Truly outstanding
Your suggestion about using an offset did confirm that the new quotes get loaded when forcing values > 68d.
Code: Select all
pushb 68d ; 44h
push 1
callasm SE_Random
pushb 69d ; NEW QUOTE LOADED!!!
Code: Select all
pushb 128d ; Returns comment 0, hard limit somewhere else...
push 1
callasm SE_Random
True that, I also use -skipintro -noabout nowadays. But some of the quotes are quite insightful, which motivated my initial foray into this thinking it would be a painless micro-mod...Jack08 wrote:P.S. This is alot of effort to add some strings the most users never really see anyway I know i always skip the egosoft into logo which skips the quote too haha.
___
Next project : testing x3galedit.obj to see if the 24*20 sector count is indeed limited by hard-coded loops like in x2story.cpp:
Code: Select all
for(loc2=0;loc2<15;loc2=loc2+1)
{
for(loc1=0;loc1<20;loc1=loc1+1)
{
...}}
Thanks again,
-lpa
PS: I can't believe you guys in the LU team actually managed to pull it off while dealing with all this asm crap... Truly outstanding
Sorry i just realized the mistake, 'pushb' can only be used to push 1 byte in size, hence b. You need to use pushw for 2 byte values or pushd for 4 byte values.
x86 has no bearing here.
x86 has no bearing here.
[ external image ]
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
Then 'pushb' must work with signed integers, otherwise the limit would be 255. I'll assume that also applies to 'pushw' and 'pushd'.Jack08 wrote:Sorry i just realized the mistake, 'pushb' can only be used to push 1 byte in size, hence b. You need to use pushw for 2 byte values or pushd for 4 byte values.
If I follow you correctly, I can't rely on x86 op-codes to edit .objs as binaries... So it's either figure out KC's keywords once compiled or get the compiler to work with larger files.Jack08 wrote:x86 has no bearing here.
Thanks!
-lpa
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
Unfortunately thoes commands are hard coded to return a constant value, they dont actually "check" anything. So unless you get to the point where you can actually teleport to one of these coordinates its likely not working.
[ external image ]
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
Agreed.Jack08 wrote:Unfortunately those commands are hard coded to return a constant value, they dont actually "check" anything. So unless you get to the point where you can actually teleport to one of these coordinates its likely not working.
I tried setenv-> and a test map with NewSector(24,20) added as text, but neither work when trying to access out-of-range sectors in the GE.
I naively replaced every occurrence of close-to-one-another 24 and 20 values, within 'obvious' loop constructs (hard to be sure when looking at asm). Some of the boundary-related code got changed, like the getMaxX-Y() return values. However, getting everything to work with such an approach is indeed next to impossible...Cycrow wrote:Those values are basically #defines, so you would need to change every occurrence of them for it to work. Which would be difficult
One question became obvious though: is there a valid reason why maxX and maxY wouldn't be defined as global vars somewhere?
They are technically global defines, but your looking at compiled output, which the compiler optimizes by in-lining statically defined global values to save a lookup. (Tho technically, yes, they are not variables, they are #define preprocessor macros because AFAIK KC has no static variable concept, only non static global variables and an very odd/fake static class concept)
[ external image ]
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
@Jack08
Thanks for confirming that. Hoping to come in before a reply, I just rushed back from being afk after realizing a compiler would substitute an internal constant for the reason you mentioned, and maybe also when the value for that constant is shorter than the lookup address (uneducated guess). It just makes sense from a logic pov, so much so that looking it up seems futile.
Damn it, looking into this stuff further demonstrates my inadequacy with every step... And also makes it impossible to go back to simply enjoying this/any game.
*head explodes*
Thanks for confirming that. Hoping to come in before a reply, I just rushed back from being afk after realizing a compiler would substitute an internal constant for the reason you mentioned, and maybe also when the value for that constant is shorter than the lookup address (uneducated guess). It just makes sense from a logic pov, so much so that looking it up seems futile.
Damn it, looking into this stuff further demonstrates my inadequacy with every step... And also makes it impossible to go back to simply enjoying this/any game.
*head explodes*
I've sent you a pm with hopefully a stable file, good luck.
[ external image ]
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
Thanks again. Did you manage to get the added sectors to be shown/visitable? I couldn't do so through the GE with the pm'd filesJack08 wrote:I've sent you a pm with hopefully a stable file, good luck.
(I hope you didn't go out of your way making these, maybe thinking they were needed elsewhere. If so, I apologize for failing to point out this is only for practice.)
I didn't test them, as for them not showing up, its likely because of the universe bod file not containing them.
I've only tried this once in the past, so i never fully figured it all out (or if was even fully possible, could have engine level hard coding preventing it from working properly)
I've only tried this once in the past, so i never fully figured it all out (or if was even fully possible, could have engine level hard coding preventing it from working properly)
[ external image ]
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
"One sure mark of a fool is to dismiss anything that falls outside his experience as being impossible."
―Farengar Secret-Fire
-
- Posts: 161
- Joined: Tue, 22. Aug 17, 13:14
I added the following to x3folder/objects/cut/00749.bod :Jack08 wrote:...its likely because of the universe bod file not containing them...
Code: Select all
p 1112; B 2521; N 2521;
{ 0x2002; 213567; -175224; 0; 0.000000; 0.000000; 0.000000; 0.000000; -1; -1; } // 0
Code: Select all
<?xml version="1.0" encoding="UTF-8" ?>
<universe>
<o f="1" t="1" x="1" y="3" r="8" size="10000000" m="0" p="77163135" qtrade="100" qfight="100" qbuild="100" qthink="100">
<o t="2" s="0" neb="0" stars="0"/>
</o>
<o f="1" t="1" x="23" y="19" r="8" size="10000000" m="0" p="1123606" qtrade="100" qfight="100" qbuild="100" qthink="100">
<o t="2" s="0" neb="0" stars="0"/>
</o>
<o f="1" t="1" x="24" y="20" r="8" size="10000000" m="0" p="77163136" qtrade="100" qfight="100" qbuild="100" qthink="100">
<o t="2" s="0" neb="0" stars="0"/>
</o>
</universe>
Oh well. I'm guessing that there must be a ton of game internals and scripts relying on hard-coded sector coordinates anyways, getting the map to work would only be the beginning of a long and painful process.
So long 10,000 sector universe!