Jump to content

UI Editing


johnnylump

Recommended Posts

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 box
2 - Base Finances
3 - Briefing
4 - Build Facilities
5 - Build Item
6 - Choose Facility
7 - Choose Technology
8 - Nothing (FLASH!)
9 - Continent Select
10 - Combat Debriefing
11 - End of Month Report
12 - Foundry
13 - Funding Council Request Screen
14 - Gollop Screen
15 - Gray Market
16 - Hiring
17 - Help or Input Dialog?
18 - Interface3d
19 - Inventory Gear Item
20 - Item Card
21 - Manufacturing
22 - Memorial
23 - Xcom "Message Box"
24 - Alerts and Doom Tracker (*Contains Interceptor Launch dialog)
25 - Item Image
26 - Objectives
27 - Officer Training School
28 - Pending Item Requests
29 - Psi Labs
30 - Recap
31 - Science Lab and Research Reports
32 - Hangars
33 - Edit Ship Loadout
34 - Ship summary screen
35 - Situation Room
36 - Situation Room HUD
37 - Soldier Customization Screen
38 - Soldier List Screen
39 - Solider Loadout Screen
40 - Soldier Promotion
41 - Soldier Summary
42 - Squad Select (*)
43 - Event updates in lower right corner
44 - Strategy HUD menus
45 - Strategy Tutorial
46 - 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 by johnnylump
Link to comment
Share on other sites

  • Replies 54
  • Created
  • Last Reply

Top Posters In This Topic

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&&register3<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


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&&register3<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)&&register1.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)&&register4.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

  • 3 weeks later...

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

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 --> 0x06BF

0x06BF = 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 choices

promotion 2 : 3 choices

promotion 3 : 3 choices

promotion 4 : 3 choices

promotion 5 : 2 choices

promotion 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

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 by Amineri
Link to comment
Share on other sites

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 by Amineri
Link to comment
Share on other sites

Interestingly, I found references to the following rank icons:

 

Texture2D gfxXComIcons.RI-01Rookie

Texture2D gfxXComIcons.RI-02Squaddie

Texture2D gfxXComIcons.RI-03Lieutenant

Texture2D gfxXComIcons.RI-04Captain

Texture2D gfxXComIcons.RI-05Major

Texture2D gfxXComIcons.RI-06Colonel

Texture2D gfxXComIcons.RI-07Commander

Texture2D 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

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

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...