johnnylump Posted April 9, 2013 Author Share Posted April 9, 2013 (edited) As an exercise, I extracted the swf code sections from XComGame.upk and Command1.upk via the above method and opened them in a flash decompiler (JPexs, freeware, seems pretty robust). XComStrategyGame has no swf sections. The swf in XComGame is one section, and it appears to be some sort of debugging interface. Here are what appears to be the primary purpose of the 46 swf code sections in Command1.upk: 1 - Message box2 - Base Finances3 - Briefing4 - Build Facilities5 - Build Item6 - Choose Facility7 - Choose Technology8 - Nothing (FLASH!)9 - Continent Select10 - Combat Debriefing11 - End of Month Report12 - Foundry13 - Funding Council Request Screen14 - Gollop Screen15 - Gray Market16 - Hiring17 - Help or Input Dialog?18 - Interface3d19 - Inventory Gear Item20 - Item Card21 - Manufacturing22 - Memorial23 - Xcom "Message Box"24 - Alerts and Doom Tracker (*Contains Interceptor Launch dialog)25 - Item Image26 - Objectives27 - Officer Training School28 - Pending Item Requests29 - Psi Labs30 - Recap31 - Science Lab and Research Reports32 - Hangars33 - Edit Ship Loadout34 - Ship summary screen35 - Situation Room36 - Situation Room HUD37 - Soldier Customization Screen38 - Soldier List Screen39 - Solider Loadout Screen40 - Soldier Promotion41 - Soldier Summary42 - Squad Select (*)43 - Event updates in lower right corner44 - Strategy HUD menus45 - Strategy Tutorial46 - End of Month Report #24 and #42 are almost certainly the most interesting ... #24 is where I tried to change the interceptor launch interface to 5 craft, and #42 is the squad select UI; if you're going to get smooth functioning of more than 6 troops, this is the place to edit. I can make the swf files I extracted available to anyone who is interested -- just pm me. Understand that this is interface editing in ActionScript, and it uses different bytecodes than the Unreal bytecodes we've been changing. I did not find any HUD-related stuff for the tactical game -- this is all strategy side. Note to self -- check XComShell.upk as another possibility. Edited April 9, 2013 by johnnylump Link to comment Share on other sites More sharing options...
twinj Posted April 11, 2013 Share Posted April 11, 2013 Have you had any success with UI edits with this method? Link to comment Share on other sites More sharing options...
johnnylump Posted April 11, 2013 Author Share Posted April 11, 2013 Welcome back! Depends on what you mean by "success" ... I've made changes and seen them have an impact in the game, but I haven't done anything I'd call meaningful. I haven't tried very hard, either. Given your work in expanding the squad size, here's a code section you might find interesting: class SquadList extends Panel { function SquadList() { register2(); register1.anchor=Environment.ANCHOR_BOTTOM_CENTER; } function onLoad() { register1.unitBoxes=new Array(); register3=0; while(register3<SquadList.MAX_UNITS) { register1.unitBoxes.push(register1["unit"+register3]); register3=register3+1; } register2.onLoad(); register1.UpdateAnchoring(); Environment.instance().SubscribeToResolutionUpdate(mx.utils.Delegate.create(register1,register1.UpdateAnchoring)); } function dispose() { Environment.instance().SubscribeToResolutionUpdate(mx.utils.Delegate.create(register1,register1.UpdateAnchoring)); register2.dispose(); } function UpdateAnchoring() { register1.setAnchor(register1.anchor); register1._y=register1._y-25; } function onPopulateDebugData() { register1.currentSelection=0; register1.debug_currentSelection=0; register1.SetUnitHelp("Icon_A_X","Edit","Icon_X_SQUARE","Clear"); register1.SetUnitInfo(0,3,"RK. HARRIES","\"Crusher\"","Support","support","BACKPACK:","Medkit","","PROMOTE"); register1.SetUnitInfo(1,0,"RK. Turbo Dwarf Kombat","\"The Beast\"","Assault","assault","BACKPACK:","Medkit","Frag Grenade",""); register1.SetUnitInfo(2,1,"RK. Savage Sushi Choreographer","\"The Beast\"","Sniper","sniper","BACKPACK:","Alien Grenade","",""); register1.SetUnitInfo(3,2,"RK. The Infernal Software Trilogy","\"The Beast\"","Heavy","heavy","BACKPACK:","Granada de fragmentación","",""); register1.SetAddUnitText(4,"ADD UNIT","+","Icon_A_X"); register1.SetAddUnitText(5,"ADD UNIT","+","Icon_A_X"); } function OnChildMouseEvent(target, event) { trace("OnChildMouseEvent: "+register3+", "+register2); if(register2==Input.MOUSE_UP) { flash.external.ExternalInterface.call("FlashRaiseMouseEvent",register1.toString(),register2,register3.toString()); } } function onInput(code, action) { if(!register2.debugging) { return false; } else { register3=true; switch(register4) { case Input.DPAD_LEFT: register0=register1.debug_currentSelection+1; register1.debug_currentSelection=register1.debug_currentSelection+1; register1.debug_currentSelection=register0%SquadList.MAX_UNITS; register1.SetSelected(register1.debug_currentSelection); break; case Input.DPAD_RIGHT: register0=register1.debug_currentSelection-1; register1.debug_currentSelection=register1.debug_currentSelection-1; register1.debug_currentSelection=(register0+SquadList.MAX_UNITS)%SquadList.MAX_UNITS; register1.SetSelected(register1.debug_currentSelection); break; default: register3=false; } return register3; } } function SetUnitInfo(index, status, unitName, nickname, classText, classLabel, backpack, item1, item2, promo) { register1.unitBoxes[register2].setIndex(register2); register1.unitBoxes[register2].setContainer(register1); register1.unitBoxes[register2].SetUnitInfo(register9,register3,register8,register7,register11,register10,register5,register4,register6); register1.unitBoxes[register2].realize(); } function SetAddUnitText(index, displayString, prefix, icon) { register1.unitBoxes[register2].setIndex(register2); register1.unitBoxes[register2].setContainer(register1); register1.unitBoxes[register2].SetAddUnitText(register4,register3,register5); register1.unitBoxes[register2].SetHelp(); register1.unitBoxes[register2].realize(); } function SetSelected(targetIndex, forceRedrawIfPreviouslySelected) { if((register3==undefined||register1.currentSelection==register3)&&!register4) { return undefined; } else { trace("SquadList - SetSelected: index="+register3); register2=0; while(register2<SquadList.MAX_UNITS) { register1.unitBoxes[register2].onLoseFocus(); register2=register2+1; } if(register3>-1&®ister3<SquadList.MAX_UNITS) { register1.currentSelection=register3; register1.unitBoxes[register3].onReceiveFocus(); register1.unitBoxes[register3].SetHelp(register1.help_icon0,register1.help_display0,register1.help_icon1,register1.help_display1); } else { if(register3==-1) { } else { trace("Error: SetSelectedItem bad : "+register3); } } } } function SetUnitHelp(icon0, display0, icon1, display1) { register1.help_icon0=register5; register1.help_icon1=register4; register1.help_display0=register3; register1.help_display1=register2; } var currentSelection = -1; var debug_currentSelection = -1; static var MAX_UNITS = 6; There are a couple of others ... Link to comment Share on other sites More sharing options...
johnnylump Posted April 11, 2013 Author Share Posted April 11, 2013 class SquadList extends Panel { function SquadList() { register2(); register1.anchor=Environment.ANCHOR_BOTTOM_CENTER; } function onLoad() { register1.unitBoxes=new Array(); register3=0; while(register3<SquadList.MAX_UNITS) { register1.unitBoxes.push(register1["unit"+register3]); register3=register3+1; } register2.onLoad(); register1.UpdateAnchoring(); Environment.instance().SubscribeToResolutionUpdate(mx.utils.Delegate.create(register1,register1.UpdateAnchoring)); } function dispose() { Environment.instance().SubscribeToResolutionUpdate(mx.utils.Delegate.create(register1,register1.UpdateAnchoring)); register2.dispose(); } function UpdateAnchoring() { register1.setAnchor(register1.anchor); register1._y=register1._y-25; } function onPopulateDebugData() { register1.currentSelection=0; register1.debug_currentSelection=0; register1.SetUnitHelp("Icon_A_X","Edit","Icon_X_SQUARE","Clear"); register1.SetUnitInfo(0,3,"RK. HARRIES","\"Crusher\"","Support","support","BACKPACK:","Medkit","","PROMOTE"); register1.SetUnitInfo(1,0,"RK. Turbo Dwarf Kombat","\"The Beast\"","Assault","assault","BACKPACK:","Medkit","Frag Grenade",""); register1.SetUnitInfo(2,1,"RK. Savage Sushi Choreographer","\"The Beast\"","Sniper","sniper","BACKPACK:","Alien Grenade","",""); register1.SetUnitInfo(3,2,"RK. The Infernal Software Trilogy","\"The Beast\"","Heavy","heavy","BACKPACK:","Granada de fragmentación","",""); register1.SetAddUnitText(4,"ADD UNIT","+","Icon_A_X"); register1.SetAddUnitText(5,"ADD UNIT","+","Icon_A_X"); } function OnChildMouseEvent(target, event) { trace("OnChildMouseEvent: "+register3+", "+register2); if(register2==Input.MOUSE_UP) { flash.external.ExternalInterface.call("FlashRaiseMouseEvent",register1.toString(),register2,register3.toString()); } } function onInput(code, action) { if(!register2.debugging) { return false; } else { register3=true; switch(register4) { case Input.DPAD_LEFT: register0=register1.debug_currentSelection+1; register1.debug_currentSelection=register1.debug_currentSelection+1; register1.debug_currentSelection=register0%SquadList.MAX_UNITS; register1.SetSelected(register1.debug_currentSelection); break; case Input.DPAD_RIGHT: register0=register1.debug_currentSelection-1; register1.debug_currentSelection=register1.debug_currentSelection-1; register1.debug_currentSelection=(register0+SquadList.MAX_UNITS)%SquadList.MAX_UNITS; register1.SetSelected(register1.debug_currentSelection); break; default: register3=false; } return register3; } } function SetUnitInfo(index, status, unitName, nickname, classText, classLabel, backpack, item1, item2, promo) { register1.unitBoxes[register2].setIndex(register2); register1.unitBoxes[register2].setContainer(register1); register1.unitBoxes[register2].SetUnitInfo(register9,register3,register8,register7,register11,register10,register5,register4,register6); register1.unitBoxes[register2].realize(); } function SetAddUnitText(index, displayString, prefix, icon) { register1.unitBoxes[register2].setIndex(register2); register1.unitBoxes[register2].setContainer(register1); register1.unitBoxes[register2].SetAddUnitText(register4,register3,register5); register1.unitBoxes[register2].SetHelp(); register1.unitBoxes[register2].realize(); } function SetSelected(targetIndex, forceRedrawIfPreviouslySelected) { if((register3==undefined||register1.currentSelection==register3)&&!register4) { return undefined; } else { trace("SquadList - SetSelected: index="+register3); register2=0; while(register2<SquadList.MAX_UNITS) { register1.unitBoxes[register2].onLoseFocus(); register2=register2+1; } if(register3>-1&®ister3<SquadList.MAX_UNITS) { register1.currentSelection=register3; register1.unitBoxes[register3].onReceiveFocus(); register1.unitBoxes[register3].SetHelp(register1.help_icon0,register1.help_display0,register1.help_icon1,register1.help_display1); } else { if(register3==-1) { } else { trace("Error: SetSelectedItem bad : "+register3); } } } } function SetUnitHelp(icon0, display0, icon1, display1) { register1.help_icon0=register5; register1.help_icon1=register4; register1.help_display0=register3; register1.help_display1=register2; } var debug_currentSelection = -1; var currentSelection = -1; static var MAX_UNITS = 6; class SquadSelect_UnitBox extends XComButton { function SquadSelect_UnitBox() { register2(); register1.Hide(); register1.bSkipMouseBinding=true; } function onLoad() { register1.promoPopup=register1.promoPopup; register1.promoIcon=register1.promoPopup.icon; register1.txtPromo=register1.promoPopup.textField_Promote; register1.backpackBG=register1.backpackMC; register1.txtName=register1.textField_Name; register1.txtNickName=register1.textField_Nickname; register1.txtClass=register1.textField_Class; register1.txtBackpack=register1.textField_Backpack; register1.txtItem1=register1.textField_Item1; register1.txtItem2=register1.textField_Item2; register1.txtAddUnit=register1.textField_AddUnit; register1.classMC=register1.classIcon; register1.default_addUnit_y=register1.txtAddUnit._y; register1.ClearAddUnitText(); register1.ClearUnitText(); register2.onLoad(); Bind.mouse(register1.buttonMC.bg,{"onRollOut":mx.utils.Delegate.create(register1,register1.onBgButtonOut),"onRollOver":mx.utils.Delegate.create(register1,register1.onBgButtonOver)}); } function onPopulateDebugData() { } function onBgButtonOver() { trace("onBgButtonOver: this="+register1._name+", index="+register1.getIndex()+", bHasMousedOut start: "+register1.bHasMousedOut); if(!(register1.getIndex()==undefined)&®ister1.bHasMousedOut) { (SquadList)register1._container.SetSelected(register1.getIndex()); flash.external.ExternalInterface.call("FlashRaiseMouseEvent",(SquadList)register1._container.toString(),Input.MOUSE_IN,register1+".onButtonOver".toString()); register1.bHasMousedOut=false; trace("CHANGED 1: bHasMousedOut: "+register1.bHasMousedOut); } } function onBgButtonOut() { trace("onBgButtonOut: this="+register1._name+", index="+register1.getIndex()+"bHasMousedOut start: "+register1.bHasMousedOut); register1.bHasMousedOut=true; trace("Changed 2 bHasMousedOut: "+register1.bHasMousedOut); } function ClearUnitText() { register1.txtName.setHTMLText(""); register1.txtNickName.htmlText=""; register1.txtNickName.htmlText=""; register1.txtClass.htmlText=""; register1.txtBackpack.htmlText=""; register1.txtItem1.__set__htmlText(""); register1.txtItem2.__set__htmlText(""); register1.txtAddUnit.htmlText=""; register1.classMC._visible=false; register1.txtPromo.htmlText=""; register1.promoPopup._visible=false; register1.backpackBG._visible=false; register1.hasPromotion=false; } function ClearAddUnitText() { register1.txtAddUnit.htmlText=""; } function SetUnitInfo(status, name, nickname, classTxt, classLabel, backpackLabel, item1, item2, promo) { register1.ClearAddUnitText(); register1.ClearUnitText(); switch(register10) { case 1: register1.gotoAndPlay("_killed"); break; case 2: register1.gotoAndPlay("_wounded"); break; case 3: register1.gotoAndPlay("_promote"); register1.hasPromotion=true; break; case 0: register1.gotoAndPlay("_normal"); } register1.txtName.setHTMLText(register4); register1.txtNickName.htmlText=register8; register1.txtClass.htmlText=register9; register1.txtBackpack.htmlText=register7; register1.txtItem1.__set__htmlText(register6); register1.txtItem2.__set__htmlText(register5); register1.txtPromo.htmlText=register2; register1.backpackBG._visible=true; if(register3=="") { register1.classMC._visible=false; } else { register1.classMC.gotoAndPlay(register3); register1.classMC._visible=true; } if(register2=="") { register1.promoPopup._visible=false; } else { register1.promoPopup._visible=true; register1.promoIcon.play(); } register1.Show(); } function CenterPromotePopup() { register1.promoPopup._x=register1.buttonMC._x+register1.buttonMC._width/2-register1.promoPopup._width/2; } function SetAddUnitText(displayString, prefix, icon) { register1.ClearAddUnitText(); register1.ClearUnitText(); register1.add_displayString=register3; register1.add_prefix=register2; register1.add_icon=register4; register1.RefreshAddUnit(); register1.Show(); } function RefreshAddUnit() { if(register1._isFocus&&!Environment.instance().IsMouseActive()) { register1.txtAddUnit.htmlText="<img src=\'"+register1.add_icon+"\' width=\'20\' height=\'20\' vspace=\'-9\'>"+register1.add_displayString; } else { register1.txtAddUnit.htmlText=register1.add_prefix+" "+register1.add_displayString; } } function onReceiveFocus() { trace("UnitBox - onReceiveFocus"); register2.onReceiveFocus(); register1.RefreshAddUnit(); } function onLoseFocus() { trace("UnitBox - onLoseFocus "); register2.onLoseFocus(); register1.RefreshAddUnit(); trace("END UnitBox - onLoseFocus "); } function Hide() { register1._visible=false; } function Show() { register1._visible=true; } function realize() { register2.realize(); if(register1.buttonMC.bg.isInit==undefined||!register1.buttonMC.bg.isInit()) { register1.buttonMC.bg.onPostInitialize=mx.utils.Delegate.create(register1,function() { Bind.mouse(register1.buttonMC.bg,{"onRollOut":mx.utils.Delegate.create(register1,register1.onBgButtonOut),"onRollOver":mx.utils.Delegate.create(register1,register1.onBgButtonOver)}); } ,register1); } else { Bind.mouse(register1.buttonMC.bg,{"onRollOut":mx.utils.Delegate.create(register1,register1.onBgButtonOut),"onRollOver":mx.utils.Delegate.create(register1,register1.onBgButtonOver)}); } switch(register1.state) { case XComButton.STATE_SELECTED: register1._y=register1.selected_y; register1.txtAddUnit._y=register1.default_addUnit_y+register1.default_y-register1.selected_y; if(register1.hasPromotion) { register1.promoPopup.gotoAndStop("_selected"); register1.buttonMC.brackets.gotoAndStop("_promote"); } else { register1.buttonMC.brackets.gotoAndStop("_normal"); } if(SquadSelect_UnitBox.ONLY_SCROLL_SELECTED_OPTION) { register1.txtName.__set__disableTextScrolling(false); } break; case XComButton.STATE_NONE: case XComButton.STATE_DISABLED: register1._y=register1.default_y; register1.txtAddUnit._y=register1.default_addUnit_y; if(register1.hasPromotion) { register1.promoPopup.gotoAndStop("_normal"); register1.buttonMC.brackets.gotoAndStop("_promote"); } else { register1.buttonMC.brackets.gotoAndStop("_normal"); } if(SquadSelect_UnitBox.ONLY_SCROLL_SELECTED_OPTION) { register1.txtName.__set__disableTextScrolling(true); } } Bind.mouse(register1.promoPopup,{"onRollOut":mx.utils.Delegate.create(register1,register1.onBgButtonOut)}); } function SetHelp(helpIcon0, helpTxt0, helpIcon1, helpTxt1) { register4=register1.buttonMC.buttonHelpContainer; register3=(XComButton)register4.editBtn; register2=(XComButton)register4.clearBtn; if(!(register4.isInit==undefined)&®ister4.isInit()) { if(register9==undefined||register9==""||register7==undefined||register7=="") { register3._visible=false; } else { register3._visible=true; } if(register8==undefined||register8==""||register6==undefined||register6=="") { register2._visible=false; } else { register2._visible=true; } if(!(register1.add_displayString=="")||!register3._visible&&!register2._visible) { register4._visible=false; return undefined; } else { register3.setStyle(XComButton.STYLE_HOTLINK_BUTTON); register2.setStyle(XComButton.STYLE_HOTLINK_BUTTON); register3.setText(register7); register3.setIcon(register9); register2.setText(register6); register2.setIcon(register8); register5=mx.utils.Delegate.create(register3,register3.mouseOut); register3.mouseOut=DelegateWithParams.create(register3,function(unitBox, origMouseOut) { register2(); register1.onBgButtonOut(); } ,register1,register5); register5=mx.utils.Delegate.create(register2,register2.mouseOut); register2.mouseOut=DelegateWithParams.create(register2,function(unitBox, origMouseOut) { register2(); register1.onBgButtonOut(); } ,register1,register5); register5=mx.utils.Delegate.create(register3,register3.mouseIn); register3.mouseIn=DelegateWithParams.create(register3,function(unitBox, origMouseIn) { register2(); register1.onBgButtonOver(); } ,register1,register5); register5=mx.utils.Delegate.create(register2,register2.mouseIn); register2.mouseIn=DelegateWithParams.create(register2,function(unitBox, origMouseIn) { register2(); register1.onBgButtonOver(); } ,register1,register5); } } else { register4._visible=false; register4.onPostInitialize=DelegateWithParams.create(register1,function(btnHelpContainer, helpIcon0, helpTxt0, helpIcon1, helpTxt1) { register6._visible=true; register1.SetHelp(register5,register3,register4,register2); } ,register4,register9,register7,register8,register6); } } var hasPromotion = false; var default_y = -145; var add_icon = ; var offscreen_help_y = 2000; var add_prefix = ; var default_help_y = 149; var selected_y = -190; var bHasMousedOut = true; var add_displayString = ; static var ONLY_SCROLL_SELECTED_OPTION = false; } Link to comment Share on other sites More sharing options...
Amineri Posted April 27, 2013 Share Posted April 27, 2013 Some good news on the UI editing front. It is much much easier (read: actually possible) to move bytes around between functions in the Actionscript code. While working on trying to add a scrollbar to the Soldier Inventory in the Loadout screen (which didn't work, unfortunately), I ended up doing a significant refactor of a section of code. I reduced the size of the constant pool, added a constant pool value ("enableScrollbar"), and resized the Onload() function. Upon applying the change, the game ran without flaw -- unfortunately, it did not add a scrollbar as I'd hoped. The original Onload() function was tiny: function onLoad() { super.onLoad(); } However, I was able to modify the overall package to make it into: function onLoad() { super.onLoad(); this.enableScrollbar(); } I did this by shortening a debug string value that was stored in the constant pool (which is kept at the very beginning of the package), shortening "InventoryList::AddInventoryItem - ERROR: Invalid ItemType." to "InventoryList::AddInvent". This freed up 33 bytes to be used in making the new call. I added the value "enableScrollbar" to the end of the constant pool (have to do this or all other constant pool references in the package will be incorrect). Adding the line this.enableScrollbar() I did using the JPEX SWF decompiler, then copied resulting hex code back out (taking advantage of its limited compiling ability). I believe this could be used to free up the space to add a slot for a fifth interceptor slot, for example. -------------------------- Unfortunately, the scrollbar functionality (particularly with two XcomList objects sharing a common background) in the Soldier loadout screen is a bit more complex. I followed the same coding as was used in the soldier list UI, but it didn't work -- back to the drawing board on that. Link to comment Share on other sites More sharing options...
Amineri Posted April 28, 2013 Share Posted April 28, 2013 I am getting a sinking feeling about our ability to hex edit some of these SWF files. It turns out that many of the commands are NOT byte-aligned, not even to 8 bit boundaries! I was trying to parse through the "normalTree" sprite definition (this defines how the regular perk tree is defined). It consists of a series of data blocks, which always start with: BF 06 14 00 00 00 Here is how this gets parsed. The first 16 bits define both the operation and the data block size, but the first TEN bits define the operation, and the next SIX bits define the block size (in bytes). However, since it is reading in 16 bits, the bytes are read in little endian, so BF 06 --> 0x06BF0x06BF = 0000011010111111 in binary bits, which is parsed into 0000011010 111111 -- so the opertation is 0000011010 = 0x1A = 26, while the size is 111111 = 0x3F = 63. In this case the 0x3Fsize means that the file size may be longer than 63 bytes, which requires that the next four bytes are read as a uint32 to get the file size. In this case the 14 00 00 00 --> 0x14 = 20 bytes This means that the BF 06 ends up being read as 0x1A and 0x3F. Very non-obvious ... ~_~ Essentially the BF 06 14 00 00 00 means that this is operation 0x1A = 26 (Placeobject2) and that the data block is 20 bytes long (starting counting after the 14 00 00 00 uint32). This means that digging through and hex editing this actionscript code is going to be somewhat painful and laborious. ---------------------------------------- The good news, however, is that I think I have figured out how to expand the perk tree by 6 perks. I was thinking of making level 0 and 5 have two perk choices, and adding a third perk choice to levels 1, 2, 3, and 4. This would mean two choices at first promotion, three choices at the next 4 level ups, and two choices at each of the final two levels. promotion 0 : 2 choices promotion 1 : 3 choicespromotion 2 : 3 choicespromotion 3 : 3 choicespromotion 4 : 3 choicespromotion 5 : 2 choicespromotion 6 : 2 choices I'll add in the capability to put a perk choice at more than one promotion level, and even the abiity to select the same perk more than once (for the "item perks"). Link to comment Share on other sites More sharing options...
Amineri Posted April 30, 2013 Share Posted April 30, 2013 (edited) Definitely some progress in the UI editing department. The mod to increase the sprite-size to accomodate up to six interceptors per continent is done and posted to the wiki. Some basic UI functionality adding more sprites to the perk selection screen has been completed (although more work needs to be done before I consider it finished enough to release). -------------------------------------- Negatives of actionscript: 1) Dealing with the bit-packing is rather cumbersome, especially as I have not yet found a good open-source SWF decompiler/recompiler that will decompile the sprites into source code. JPEX allows graphical viewing of sprites, and decompiles scripts to source code, but I've had to decompile sprites by hand. 2) There does not appear to be a null-op code. The overall Command1.upk size has to remain the same (I think), which can make some edits tricky if the new code is smaller than the old code. Since most references are made via strings, there are usually workarounds to get the size to match up. Positives of actionscript: 1) Actionscript code is a lot less finicky about function boundaries than the UE code is. There does not appear to be a master header anywhere declaring where each sprite / function is defined. I've had great success stealing bytes from one function and adding them to another. 2) Actionscript makes extensive use of strings as identifiers. This is the primary method by which references between different parts of the code are made. In some cases it is possible to recover bytes by shortening these string identifiers. For the perk tree, I shortened the reference string 'promotionBracket' to just 'p', saving 15 bytes each, over 7 calls. This gave me the space to add 4 additional perk icons to the overall tree sprite. 3) Actionscript doesn't appear to have the distinction between file bytes and virtual bytes (like Unreal Script does), making it much simpler to recalculate offsets. ----------------------------------------------- I found the following website very useful as a reference when trying to understand the SWF bytecodes -- particular with regard to the sprites: http://www-lehre.inf.uos.de/~fbstark/diplom/docs/swf/Intro.htm It doesn't have much information regarding the scripts -- the basic arithmetic operators and control structures aren't there, but that information can be readily obtained from the JPEX Flash Decompiler. Edited April 30, 2013 by Amineri Link to comment Share on other sites More sharing options...
Amineri Posted May 1, 2013 Share Posted May 1, 2013 (edited) Regarding icons -- I found the following reference in the portion of Comman1.upk related to the perk tree: \gfxXComIcons\XComIcons.swf It appears that there was a dedicated swf file for the XCOM Icons. If we can find this section of file, it might be possible to replace or even add in additional icons. I looked through the files, but haven't seen anything obvious jumping out at me yet. EDIT: I just found some interesting tag labels:Texture2D gfxXComIcons.Sentinel.Texture2D gfxXComIcons.savior And a bunch of others. These were in GlobalPersistentCookerData.upk. These text strings appear to be part of the header of a swf tag (they are just strings listed one after another). In addition to perk icons, I also saw 2D UIFlag icons, and a bunch of "narrative moments" Edited May 1, 2013 by Amineri Link to comment Share on other sites More sharing options...
Amineri Posted May 1, 2013 Share Posted May 1, 2013 Interestingly, I found references to the following rank icons: Texture2D gfxXComIcons.RI-01RookieTexture2D gfxXComIcons.RI-02SquaddieTexture2D gfxXComIcons.RI-03LieutenantTexture2D gfxXComIcons.RI-04CaptainTexture2D gfxXComIcons.RI-05MajorTexture2D gfxXComIcons.RI-06ColonelTexture2D gfxXComIcons.RI-07CommanderTexture2D gfxXComIcons.RI-08FieldMarshal Apparently at some point there were 8 levels planned.Corporal isn't mentioned here, so there might be some mismatching between this name and in-game rank name. That last rank icon might still be floating about, though... Link to comment Share on other sites More sharing options...
bokauk Posted May 1, 2013 Share Posted May 1, 2013 I just started looking into modifying the SWF files yesterday and I'm way behind with what's happened over the past couple of months, so I'm struggling to keep up with everything! Are people still using dumbo111's method of stripping the SWFs out of the UPKs manually, one by one and replacing the first 3 bytes with FWS? If you haven't already found a SWF extractor, I could write one if it'd be useful and you aren't already using a tool that does this. It's funny you mention the gfxXComIcons directory because I have it open at the moment and is where I found the perk icons I'm using for the Perk Tree Builder, so I doubt you'll find the icons in the SWF files too, but I could be wrong, I currently know very little about SWF editing :smile: I'm not sure if you already know, but you can use Umodel to extract the game assets (including textures) from the UPKs, if that's what you're after :smile: There are so many things I want to look into, but I want to get this Perk Tree builder finished first. Hopefully once that's done, I'll be of more use helping with other bits, although I've got a lot of catching up to do! :smile: RI-08FieldMarshall.tga http://i.imgur.com/NkZ5TdG.jpg Link to comment Share on other sites More sharing options...
Recommended Posts