Amineri Posted April 6, 2013 Author Share Posted April 6, 2013 I found an alternate way to free up the bytes. It involves modifying the conditional to remove the "interceptor-type" item: change: if(kInt.IsFirestorm()){ STORAGE().RemoveItem(106, 1);}else{ STORAGE().RemoveItem(105, 1);} into m_iJetsLost = 105;if(kInt.IsFirestorm()){ ++ m_iJetsLost; }STORAGE().RemoveItem(m_iJetsLost, 1); The STORAGE() call with a byte int was 31 bytes, so the new call with a class variable is 34 bytes. Removing the else saves 3 bytes. The variable assignment is 7 bytes, and the increment operation is 6, for a total byte savings of 18. Combined with the 10 bytes saved by changing to direct access to m_kEntity instead of the function call to GetEntity() brings the total savings to 28 bytes. This means that a better conditional can be used, instead of if(!m_bNarrLostJet), can directly use: if(kInt.m_iHP > 0) which is 25 bytes to implement. This means that the OnInterceptorDestroyed call can be left alone, and we don't have to worry about any possibly funky timing with m_bNarrJetsLost. The side effect of this is that the m_iJetsLost class variable is overwritten. This variable is only used to trigger a narrative moment if 2 or more interceptors are lost, in XGMissionControlUI.UpdateView. However, the information really isn't lost, because the total number of interceptors lost was also saved in the RecapData via the STAT_AddStat(35,1) call. This means a later assignment of m_iJetsLost = STAT_GetStat(35); will correct the class variable. That line is 18 bytes, and can happen anywhere later in the call sequence that is still in scope of XGFacility_Hanger. Link to comment Share on other sites More sharing options...
johnnylump Posted April 6, 2013 Share Posted April 6, 2013 (edited) Bravo! You are a virtuosa at this. Edit: Bradford talking about an interceptor being shot down is bugged -- it doesn't happen when it goes down, but he talks about it MUCH later, when you leave and return to the Geoscape, or enter the Situation Room, or something (can't recall precisely). Edited April 7, 2013 by johnnylump Link to comment Share on other sites More sharing options...
Amineri Posted April 6, 2013 Author Share Posted April 6, 2013 I've figured out where to fix the m_iJetsLost class variable. The UpdateHangerBays() call in OnInterceptorLost() was completely unnecessary, as UpdateHangerBays() is called at the very end of RemoveInterceptors(). UpdateHangerBays() is 10 file bytes, and the m_iJetsLost += 1; is 8 bytes, freeing up exactly the space needed to call m_iJetsLost = STAT_GetStat(35) at the end of the function. The new OnInterceptorLost() would look like: RemoveInterceptor(kInterceptor);m_bNarrLostJet = true;GEOSCAPE().ResetShipTimeScale();STAT_AddStat(35, 1);m_iJetsLost = STAT_GetStat(35); Time to build the hex code! Link to comment Share on other sites More sharing options...
Amineri Posted April 7, 2013 Author Share Posted April 7, 2013 Here is the hex code change for RemoveInterceptor: original4F 2B 00 00 AB 1F 00 00 00 00 00 00 4E 2B 00 00 00 00 00 00 00 00 00 00 4F 2B 00 00 00 00 00 00 13 03 00 00 CE 5C 00 00 84 01 00 00 30 01 00 00 56 01 CB 2A 00 00 0A 00 00 4F 2B 00 00 16 07 5E 00 19 00 4F 2B 00 00 0A 00 30 3E 00 00 00 1B 5D 14 00 00 00 00 00 00 16 19 1B 23 27 00 00 00 00 00 00 16 0D 00 00 00 00 00 00 1B B0 22 00 00 00 00 00 00 2C 6A 26 16 06 81 00 19 1B 23 27 00 00 00 00 00 00 16 0D 00 00 00 00 00 00 1B B0 22 00 00 00 00 00 00 2C 69 26 16 19 1B 23 27 00 00 00 00 00 00 16 2C 00 00 00 00 00 00 1B A2 00 00 00 00 00 00 00 38 3A 19 00 4F 2B 00 00 09 00 10 3E 00 00 00 01 10 3E 00 00 4A 4A 16 07 36 01 77 19 00 4F 2B 00 00 0A 00 23 00 00 00 00 1B 04 0F 00 00 00 00 00 00 16 2A 16 19 00 4F 2B 00 00 0B 00 00 00 00 00 00 1B 42 11 00 00 00 00 00 00 27 16 19 19 00 4F 2B 00 00 0A 00 23 00 00 00 00 1B 04 0F 00 00 00 00 00 00 16 03 00 B7 FF FF FF 00 61 17 16 19 00 4F 2B 00 00 0A 00 00 00 00 00 00 1B BA 07 00 00 00 00 00 00 16 19 00 4F 2B 00 00 03 00 B7 FF FF FF 00 61 17 16 1B CC 22 00 00 00 00 00 00 16 1B C4 2B 00 00 00 00 00 00 16 04 0B 53 new RemoveInterceptor hex code consolidate, with jump offsets corrected4F 2B 00 00 AB 1F 00 00 00 00 00 00 4E 2B 00 00 00 00 00 00 00 00 00 00 4F 2B 00 00 00 00 00 00 13 03 00 00 CE 5C 00 00 A0 01 00 00 30 01 00 00 56 01 CB 2A 00 00 0A 00 00 4F 2B 00 00 16 0F 01 CA 2A 00 00 2C 69 07 4F 00 19 00 4F 2B 00 00 0A 00 30 3E 00 00 00 1B 5D 14 00 00 00 00 00 00 16 A3 01 CA 2A 00 00 16 19 1B 23 27 00 00 00 00 00 00 16 0D 00 00 00 00 00 00 1B B0 22 00 00 00 00 00 00 01 CA 2A 00 00 26 16 07 DF 00 97 19 00 4F 2B 00 00 09 00 78 3D 00 00 00 01 78 3D 00 00 25 16 19 1B 23 27 00 00 00 00 00 00 16 2C 00 00 00 00 00 00 1B A2 00 00 00 00 00 00 00 38 3A 19 00 4F 2B 00 00 09 00 10 3E 00 00 00 01 10 3E 00 00 4A 4A 16 07 50 01 77 19 00 4F 2B 00 00 09 00 1F 00 00 00 00 01 1F 00 00 00 2A 16 19 00 4F 2B 00 00 0B 00 00 00 00 00 00 1B 42 11 00 00 00 00 00 00 27 16 19 19 00 4F 2B 00 00 09 00 1F 00 00 00 00 01 1F 00 00 00 03 00 B7 FF FF FF 00 61 17 16 19 00 4F 2B 00 00 0A 00 00 00 00 00 00 1B BA 07 00 00 00 00 00 00 16 19 00 4F 2B 00 00 03 00 B7 FF FF FF 00 61 17 16 1B CC 22 00 00 00 00 00 00 16 1B C4 2B 00 00 00 00 00 00 16 04 0B 0B 0B 53 It decompiled like so for me: function RemoveInterceptor(XGShip_Interceptor kInt){ m_arrInts.RemoveItem(kInt); m_iJetsLost = 105; // End:0x4F if(kInt.IsFirestorm()) { ++ m_iJetsLost; } STORAGE().RemoveItem(m_iJetsLost, 1); // End:0xDF if(kInt.m_iHP > 0) { STORAGE().AddItem(kInt.m_eWeapon); } // End:0x150 if(kInt.m_kEntity != none) { kInt.HideEntity(true); kInt.m_kEntity.Destroy(); } kInt.DestroyHangarShip(); kInt.Destroy(); ReorderCraft(); UpdateHangarBays(); return; } I've verified that the game doesn't crash on startup, but no further (and I have to stop working on this for a bit @_@ ). If someone wants to test it out, it would be much appreciated :smile: . Also, I haven't started the hex code to fix the m_iJetsLost assignment, so this may cause Bradford to chide you about getting the Firestorm a bit earlier than he should. However, I'd like to know that this code works before messing with the other. Enjoy! P.S. I feel dumb, but for the life of me cannot figure out how to change the title of the forum topic ... help?? Link to comment Share on other sites More sharing options...
anUser Posted April 7, 2013 Share Posted April 7, 2013 great man! hehe get some rest, you deserve it! so in the end it's all condensed in just this function? ... an advice, don't underestimate the power of the hex... I've been stuck for the last two hours with a function that decompiles well but always crash on run time. I know what sentence is the b¡tch, but no matter how I writte it, I'm trying all sentences that decompile well all the time but the b¡tch keeps kicking me out to desktop... well, good luck commander!.. changing the title, nah, I don't think that's possible. Link to comment Share on other sites More sharing options...
XMarksTheSpot Posted April 7, 2013 Share Posted April 7, 2013 m_iJetsLost = 105;if(kInt.IsFirestorm()){ ++ m_iJetsLost; }STORAGE().RemoveItem(m_iJetsLost, 1);I suspect this part could be further condensed into a single line using the ternary (conditional) operator: STORAGE().RemoveItem(kInt.IsFirestorm() ? 106 : 105, 1); I don't actually know the hex values for that operator or whether it's actually usable in XCOM's version of UE3, but maybe one of you does :smile: Link to comment Share on other sites More sharing options...
Amineri Posted April 7, 2013 Author Share Posted April 7, 2013 Excellent, excellent suggestion! There are a bunch of examples in the hex code for ternary operations. This would also save having to use the class variable, simplifying things considerably. Here is some sample code for the snippet: ((fWidth > 0.0) ? fWidth : kDefaultDecalScale); 45 B1 00 58 01 00 00 1E 00 00 00 00 16 09 00 00 58 01 00 00 09 00 00 51 01 00 00 This breaks down to: 45 -- conditional token -- this is the key hex for the ternary operator, distinct from the 07 jump-if-not token used for if statementB1 00 58 01 00 00 1E 00 00 00 00 16 -- fWidth > 0.009 00 -- not reported by UE Explorer00 58 01 00 00 -- local variable fWidth09 00 -- not reported by UE Explorer00 51 01 00 00 -- local variable fWidth Although not reported by UE explorer in the Hex Viewer, it appear that the 09 00 hex pair is used to separate the three parts of the ternary operator: 45 (conditional) 09 00 (value_true) 09 00 (value_false) Now that I know how to interpret what I'm seeing, I see the entry : EX_Conditional = 0x45; in the Bytecode Table. Looking at a more complex construction: ((HumanPlayer != none) ? HumanPlayer.GetActiveUnit() : none); returns hex code:45 -- conditional token77 00 74 09 00 00 2A 16 -- HumanPlayer! != none1F 00 -- not reported by UE explorer19 00 74 09 00 00 0A 00 37 94 00 00 00 1B EB 30 00 00 00 00 00 00 16 -- HumanPlayer.GetActiveUnit()01 00 -- not reported by UE explorer2A -- 'none' token In this case the format is:45 (conditional) 1F 00 (return_true) 01 00 (return_false) It appears as the two separating bytes are a relative offset, measured in virtual bytes (not file bytes). This is similar to how the EX_Skip = 0x18; optimization token is used in boolean expressions. In the first example, both return values were local variables, so their virtual size was 0x0009 (5 file + 4 when loaded). In the second example, the more complex construction of HumanPlayer.GetActiveUnit() has a virtual size of 0x001F = 31 (23 file + 8 when loaded), but the simpler return value of 4A 'none' has a virtual size of 0x0001 --------------------------------------------------------------------------------- The general construction of the ternary operator : (conditional) ? (return_true) : (return_false) in hex is: 45 -- conditional token(conditional) -- hex that evaluates to boolean true/false## ## -- two bytes containing the virtual size of (return_true)(return_true) -- hex expression## ## -- two bytes containing the virtual size of (return_false)(return_false) -- hex expression For the case in the interceptor code (a common use, I'd guess), the hex will read:45 (conditional kInt.IsFirestorm()) 02 00 2C 6A 02 00 2C 69 As with 0F (Let token) and 07 (Jump-if-not token), no 16 execution operator is needed for the 45 (conditional token). Once the third value is read by the interpreter, the 45 conditional token executes 'automatically'. The total number of bytes added (above the conditional + two values) is only five bytes, which is amazing! :o I'll likely put out some revised hex for this, as it fixes the issue of having to use the m_iJetsLost class variable. I can see this being useful in oh-so-many ways... Link to comment Share on other sites More sharing options...
Amineri Posted April 7, 2013 Author Share Posted April 7, 2013 Updated version, that incorporates the new ternary conditional. This eliminates the usage of any class variables, so no other changes are needed. Change to XcomStrategyGame.upk >> XGFacility_Hanger.RemoveInterceptor original4F 2B 00 00 AB 1F 00 00 00 00 00 00 4E 2B 00 00 00 00 00 00 00 00 00 00 4F 2B 00 00 00 00 00 00 13 03 00 00 CE 5C 00 00 84 01 00 00 30 01 00 00 56 01 CB 2A 00 00 0A 00 00 4F 2B 00 00 16 07 5E 00 19 00 4F 2B 00 00 0A 00 30 3E 00 00 00 1B 5D 14 00 00 00 00 00 00 16 19 1B 23 27 00 00 00 00 00 00 16 0D 00 00 00 00 00 00 1B B0 22 00 00 00 00 00 00 2C 6A 26 16 06 81 00 19 1B 23 27 00 00 00 00 00 00 16 0D 00 00 00 00 00 00 1B B0 22 00 00 00 00 00 00 2C 69 26 16 19 1B 23 27 00 00 00 00 00 00 16 2C 00 00 00 00 00 00 1B A2 00 00 00 00 00 00 00 38 3A 19 00 4F 2B 00 00 09 00 10 3E 00 00 00 01 10 3E 00 00 4A 4A 16 07 36 01 77 19 00 4F 2B 00 00 0A 00 23 00 00 00 00 1B 04 0F 00 00 00 00 00 00 16 2A 16 19 00 4F 2B 00 00 0B 00 00 00 00 00 00 1B 42 11 00 00 00 00 00 00 27 16 19 19 00 4F 2B 00 00 0A 00 23 00 00 00 00 1B 04 0F 00 00 00 00 00 00 16 03 00 B7 FF FF FF 00 61 17 16 19 00 4F 2B 00 00 0A 00 00 00 00 00 00 1B BA 07 00 00 00 00 00 00 16 19 00 4F 2B 00 00 03 00 B7 FF FF FF 00 61 17 16 1B CC 22 00 00 00 00 00 00 16 1B C4 2B 00 00 00 00 00 00 16 04 0B 53 new RemoveInterceptor hex code consolidate, with jump offsets corrected4F 2B 00 00 AB 1F 00 00 00 00 00 00 4E 2B 00 00 00 00 00 00 00 00 00 00 4F 2B 00 00 00 00 00 00 13 03 00 00 CE 5C 00 00 94 01 00 00 30 01 00 00 56 01 CB 2A 00 00 0A 00 00 4F 2B 00 00 16 19 1B 23 27 00 00 00 00 00 00 16 0D 00 00 00 00 00 00 1B B0 22 00 00 00 00 00 00 45 19 00 4F 2B 00 00 0A 00 30 3E 00 00 00 1B 5D 14 00 00 00 00 00 00 16 02 00 2C 6A 02 00 2C 69 26 16 07 C5 00 97 19 00 4F 2B 00 00 09 00 78 3D 00 00 00 01 78 3D 00 00 25 16 19 1B 23 27 00 00 00 00 00 00 16 2C 00 00 00 00 00 00 1B A2 00 00 00 00 00 00 00 38 3A 19 00 4F 2B 00 00 09 00 10 3E 00 00 00 01 10 3E 00 00 4A 4A 16 07 36 01 77 19 00 4F 2B 00 00 09 00 1F 00 00 00 00 01 1F 00 00 00 2A 16 19 00 4F 2B 00 00 0B 00 00 00 00 00 00 1B 42 11 00 00 00 00 00 00 27 16 19 19 00 4F 2B 00 00 09 00 1F 00 00 00 00 01 1F 00 00 00 03 00 B7 FF FF FF 00 61 17 16 19 00 4F 2B 00 00 0A 00 00 00 00 00 00 1B BA 07 00 00 00 00 00 00 16 19 00 4F 2B 00 00 03 00 B7 FF FF FF 00 61 17 16 1B CC 22 00 00 00 00 00 00 16 1B C4 2B 00 00 00 00 00 00 16 04 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 53 New decompiled code should read: function RemoveInterceptor(XGShip_Interceptor kInt){ m_arrInts.RemoveItem(kInt); STORAGE().RemoveItem(((kInt.IsFirestorm()) ? 106 : 105), 1); // End:0xC5 if(kInt.m_iHP > 0) { STORAGE().AddItem(kInt.m_eWeapon); } // End:0x136 if(kInt.m_kEntity != none) { kInt.HideEntity(true); kInt.m_kEntity.Destroy(); } kInt.DestroyHangarShip(); kInt.Destroy(); ReorderCraft(); UpdateHangarBays(); return; } The new code segment has 16 extra 0Bs at the end (in addition to the 25 bytes used for the new code, adding up to 41 total bytes freed up). Code has been verified to not crash and NOT delete interceptor weapons when an interceptor is dismissed from the hanger. Thank you everyone for your help! Link to comment Share on other sites More sharing options...
johnnylump Posted April 8, 2013 Share Posted April 8, 2013 (edited) <p>NVM -- this seems to be working. My bad, and well done.</p> Edited April 8, 2013 by johnnylump Link to comment Share on other sites More sharing options...
anUser Posted April 9, 2013 Share Posted April 9, 2013 Great finding, this ternary conditional. Thanks for hex code as well, I'll def give it some use. I forgot to mention another way of saving bytes that I've tested is using implicit casting, specialy for bools (0 & 1 if cast to int), so that sentence of yours could be written: STORAGE.RemoveItem(105 + kInt.IsFirestorm(), 1) or 106 - isFirestorm() whateverI know it's late now and you may already know that but I thought I'd post it as well just in case it may help Link to comment Share on other sites More sharing options...
Recommended Posts