Big thanks for all the rep!
.Unpacker By Alpaca and using it.
Detailed Instructions on install python,and using the unpacker with exact details.
Spoiler Alert, click show to read:
- (XP)Find you 'steamapps' folder, (just search for it, or find it in your program files under steam), then from there, goto 'common\empire total war demo'
- You should now have FOUR folders and an assortment of files in front of you. You want to copy the entire 'data' folder and paste it into parent folder (empire total war demo folder) and rename it backup.
Spoiler Alert, click show to read:- Now you need to install Python 3.01 if you havn't got it installed already. Goto 'HERE' and click the download that relates you to (x84 is for 32-bit standard XP and Vista).
- Install Python 3.01 with all the default settings nothing needs to be changed.
- Once installed, open up your 'empire total war demo' folder again and then go into your 'data' folder but not the backup.
- Now create a file called 'emp_unpacker.py' and right-click "Edit with IDLE."
Spoiler Alert, click show to read:- Open up the latest revision of the code (#3 at time of writing this) and copy and paste it into the window. (See Below, "Updated Python Source Code (Revision #)"
Spoiler Alert, click show to read:- Save and exit the program and the 'Python Shell' program.
- Open up the command prompt (XP - Start - Run - 'cmd' ) (Vista, goto the 'data' folder, hold SHIFT+Right click and choose 'Open command prompt here).
- XP users need to change the command prompts directory to the data's folder.
Mine is 'E:\Program Files (x86)\Steam\steamapps\common\empire total war demo\data'
So I type, "cd /d E:\Program Files (x86)\Steam\steamapps\common\empire total war demo\data". Notice the text before the '>' has changed. If the directory is on your C: drive, you do not need to use the /d switch so it would be
"cd C:\Program Files (x86)\Steam\steamapps\common\empire total war demo\data"
Spoiler Alert, click show to read:- Now Vista and XP users are both in the 'data' directory and have python install you need to enter the following command precisely
'emp_unpacker.py -u all' . If this fails for one reason or another please see trouble-shooting below.- The command prompt should now show lots of files being unpacked into an 'unpacked' folder.
- Once finished, copy the contents of the 'unpacked' folder (copy, not cut!)across into the main data folder. Select Yes over any warnings of the same folder ect.
- Rename demo1.pack 'backup_demo1' without an extension, people are still missing this. I mean, when you rename it, rename it simpy 'backup_demo1'. NOT 'backup_demo1.pack'!!!
- Congratulations, you've now set-up your modding environment! .
When to use the Backups.
Spoiler Alert, click show to read:
If you mess up a single file, simply copy the old file across from the 'unpacked' directory,or want to revert back to the standard demo with the unpacked files, copy and paste the entire unpacked directory into the data directory overwriting all the old files. Fresh modding environment!
However,
If you wish to revert (For what ever reason) to the standard pack files with no messy unpacked files, delete the data folder and rename the BACKUP folder we created in step 2 to 'data'.
MOD: Edit Unit Present in Battles. - Changing units, or unit numbers.
Spoiler Alert, click show to read:
In the Two default battles;
Battle of Brandywine.
Battle of Lagos.
We can simply and quickly edit the amount of units and their type.
We'll use Brandywine as an example.
Open up 'battle_of_brandywine.xml' with WordPad or similar program.
Syntax:
A unit is defined by <unit> to </unit>. Here is the British Generals unit
Spoiler Alert, click show to read:Code:<unit unit_category="cavalry" num_soldiers="16" script_name="britain_euro_generals_bodyguard"> <unit_type type="euro_generals_bodyguard"/> <position x="-126.45" y="522.35"/> <orientation radians="2.89"/> <width metres="15.82"/> <unit_capabilities> </unit_capabilities> <general> <name>William Howe</name> <experience>0</experience> <portrait>data/ui/portraits/european/Cards/general/old/025.tga</portrait> </general> <unit_experience level="0"/> </unit>
From that we can change,
- unit_category; "infantry","cavalry","dragoons","artillery". (Not sure what effect this has yet)
- num_soldiers;"X" where X is a number, we have yet to find a limit to this. Simply change the number inside the quotes to effect the amount of men in that unit. (NOTE, Unit cards prior to battle seem not to update.
Spoiler Alert, click show to read:- script_name;unless you plan on controlling the unit by script this is slightly redundant.
- unit_type type; "UNITTYPE", where unit type is a defined unit type, see reference below.
- position;starting location on the map.
- orientation radians;its rotation on initial map setup? (Yet to see).
- width metres; how wide the formation is.
- unit_capabilities; not directly edited, defined by whats contained within the tags (a tag is a <[TEXT]> to </[TEXT]>, like how you use BBcode in the forum posts. See reference below.
- unit_experience level;change number in quotes from 0-9. Affects the units experience level.
MOD: Add Units Present in Battles. - Add extra units in a army.
Spoiler Alert, click show to read:
This is a really simple one, in the battle .xml file again. battle_of_brandywine.xm for example in data/Scenarios.
You'll see the fourth tag is '<army>' then <fraction>british</fraction>, so this '<army>' tag is defining the British army present at the battle.
As you look down further you'll see the <unit> tags
Spoiler Alert, click show to read:Code:<unit unit_category="infantry" num_soldiers="80" script_name="britain_euro_line_infantry_01"> <unit_type type="euro_line_infantry_britain"/> <position x="-108.22" y="501.29"/> <orientation radians="2.66"/> <width metres="29.58"/> <unit_capabilities> <firing_drill>fire_volley</firing_drill> <special_ability>square_formation</special_ability> <bayonet_type>ring_bayonets</bayonet_type> </unit_capabilities> <unit_experience level="0"/> </unit>
Too add a unit, simply copy and paste one of the complete unit tags (as shown in the spoiler) before the </army> tag.
(Of course, scroll down to America to give them units).
MOD: Add a Army Present in Battles. - Add new armies (Ally/enemy).
Spoiler Alert, click show to read:
Spoiler Alert, click show to read:
In the battle XML File.
The first TAG we see is <alliance id="0">, these define the 'Team' numbers as they were in M2TW custom battles.
All armies inside the <alliance id="0"></alliance> tag will be on the same side.
An army is defined between the <army> and </army> tags, but they are setup with
Spoiler Alert, click show to read:Code:<faction>britain</faction> <deployment_area> <centre x="0" y="400"/> <width metres="1000.0"/> <height metres="500.0"/> <orientation radians="-0.1047"/> </deployment_area> <camera_start_position x="-122" y="33.2" z="566.6" /> <camera_target_position x="-156" y="-31.7" z="384.4" />
Adding a new Army for the battle screen is as simple as adding a unit to a army.
Simple copy and paste the following template.
Spoiler Alert, click show to read:Code:<army> <faction>britain</faction> <deployment_area> <centre x="0" y="400"/> <width metres="1000.0"/> <height metres="500.0"/> <orientation radians="-0.1047"/> </deployment_area> </army>
and place it BEFORE the </alliance> tag.
Too add a unit do this,
Spoiler Alert, click show to read:Code:<army> <faction>britain</faction> <deployment_area> <centre x="0" y="400"/> <width metres="1000.0"/> <height metres="500.0"/> <orientation radians="-0.1047"/> </deployment_area> <unit unit_category="cavalry" num_soldiers="16" script_name="britain_euro_generals_bodyguard"> <unit_type type="euro_generals_bodyguard"/> <position x="-126.45" y="522.35"/> <orientation radians="2.89"/> <width metres="15.82"/> <unit_capabilities> </unit_capabilities> <general> <name>William Howe</name> <experience>0</experience> <portrait>data/ui/portraits/european/Cards/general/old/025.tga</portrait> </general> <unit_experience level="0"/> </unit> </army>
This gives our new army one unit of 'euro_generals_bodyguard'. Note, the first unit defined is the commander from what I see.
You can also add alliances (<alliance id="0"></alliance>,<alliance id="1"></alliance>,<alliance id="3"></alliance>,ect..)
DO NOTE! You should edit the deployment area.
Also remove the <skip-deployment > tags,
and in the 'battle_of_brandywine.battle_script' files, DELETE EVERYTHING after
-----------------------------------------------------------
-- CONTROLLER SETUP
-----------------------------------------------------------
-- FRIENDLY UNIT CONTROLLERS
friendly_unit = {};
for a = 1,16 do
friendly_unit[a] = friendly_units:item(a);
end
MOD: Using Fortifications in Battles. - Use the deployment stage real time fortifications.
Spoiler Alert, click show to read:
There are three currently working fornications that we can set-up in the deployment stage.
These are,
Earthworks/Trenches:
Spoiler Alert, click show to read:
Gabionade:
Spoiler Alert, click show to read:
Chevaux De Frise: (Larger wooden spikes).
Spoiler Alert, click show to read:
To use all three, add them to a units (earthworks and chevaux to infantry, gabionade to artillery only).
The result?<special_ability>earthworks</special_ability>
Spoiler Alert, click show to read:
Ensure that in the battle .xml file, <skip-deployment> is REMOVED! This allows you to deploy your troops and more importantly, setup your fortifications.
When in deployment mode, click your unit and you'll have a little bar show up, click the relative icon and the fortification will spawn in front of that unit. If you move the unit it will move too.
and in the 'battle_of_brandywine.battle_script' files, DELETE EVERYTHING after
-----------------------------------------------------------
-- CONTROLLER SETUP
-----------------------------------------------------------
-- FRIENDLY UNIT CONTROLLERS
friendly_unit = {};
for a = 1,16 do
friendly_unit[a] = friendly_units:item(a);
end
REFERENCE: Unit Types
Spoiler Alert, click show to read:
Artillery:
- episodic_demi_cannon
- 24_lber_land_cannon
- 6_lber_land_cannon_galloper
Infantry:
- euro_generals_bodyguard
- euro_grenadiers_bearskin
- euro_highlanders
- guard_infantry
- euro_line_infantry_britain
- americas_hessian_infantry
- euro_light_dragoons
- euro_hussars
- euro_light_infantry
- americas_long_rifle_men
- americas_line_infantry
- euro_militia_infantry_minutemen
- euro_militia_infantry_colonial_tutorial_rubbish
- euro_light_infantry_barbets
- euro_rangers_frontiersmen
Ships:
- 1st_rate_admiral_british
- 3rd_rate_admiral_british
- 1st_rate_british
- 2nd_rate_british
- 3rd_rate_british
- 4th_rate_british
- 6th_rate_british
- 3rd_rate_admiral_french
- 3rd_rate_french
- 4th_rate_french
- sloop_french
REFERENCE: Unit Capabilities
Spoiler Alert, click show to read:
A units capabilities define its special abilities (planting wooden stakes) and how it acts on the battlefield (firing drills).
For Infantry, there are usualy no more than three containers within this;
Spoiler Alert, click show to read:Code:<firing_drill>fire_volley</firing_drill> <special_ability>square_formation</special_ability> <bayonet_type>ring_bayonets</bayonet_type>
For artillery, four.
Spoiler Alert, click show to read:Code:<firing_drill>fire_volley</firing_drill> <special_ability>unlimber</special_ability> <shot_type>round_shot</shot_type> <shot_type>canister</shot_type>
I'm still working out from my list, but these are ones I know work.
Firing Drills
- fire_volley
- rank_fire
Special Abilities
- wooden_stakes
- fire_and_advance
- square_formation
Bayonet Type
- ring_bayonets
- socket_bayonets
- plug_bayonets
Artillery Shot Type
- canister
- round_shot
- sharpnel_shot
Undefined list of found special abilities and drills
Spoiler Alert, click show to read:
chevaux_de_frise
diamond_formation
wedge_formation
earthworks
fire_and_advance
fire_mounted
fire_volley
fougasse_basic
fougasse_improved
fougasse_basic
gabionade
improved_platoon_fire_grouped
platoon_fire_grouped
mass_fire
fire_volley
pike_square_formation
pike_wall_formation
platoon_fire_grouped
rank_fire
plug_bayonets
rank_fire
mass_fire
ring_bayonets
plug_bayonetssocket_bayonets
ring_bayonetssquare_formation
pike_square_formationwedge_formation
wooden_stakes
light_infantry_behaviour
REFERENCE: Naval | Lighting/Weather Presets
Spoiler Alert, click show to read:
There are five different sets of weather/lighting in the scenarios:
Land Tutorial
Naval Tutorial
Porto Novo
Brandywine
Lagos
Here are screenshots and the code for each one. This code you can splice in to your battle xml files. I only tested this with naval battles. Note, not included in these are the sea_surface_name entries. These aren't necessary to change, and can be set to either 2 or 3 as far as I know. I tested 0,1,2,3,4,6,9
-Garnier
Land Tutorial:
Spoiler Alert, click show to read:
Code:<battle_description> <time_of_day>midday</time_of_day> </battle_description> <weather> <prevailing_wind x="0" y="9"/> <lighting>default</lighting> <environment_key>day_clear_1_dry_summer</environment_key> </weather>
Naval Tutorial:
Spoiler Alert, click show to read:
Code:<battle_description> <time_of_day>afternoon</time_of_day> </battle_description> <weather> <prevailing_wind x="1" y="9"/> <lighting>naval</lighting> <environment_key>sc_tropic_day_dry_2_summer</environment_key> </weather>
Porto Novo:
Spoiler Alert, click show to read:
Code:<battle_description> <time_of_day>morning</time_of_day> </battle_description> <weather> <prevailing_wind x="0" y="5"/> <lighting>naval</lighting> <environment_key>sc_tropic_coast_morning_clear_0_dry_summer</environment_key> </weather>
Brandywine:
Spoiler Alert, click show to read:
Code:<battle_description> <time_of_day>midday</time_of_day> </battle_description> <weather> <prevailing_wind x="0" y="9"/> <lighting>default</lighting> <environment_key>day_clear_2_dry_summer</environment_key> </weather>
Lagos:
Spoiler Alert, click show to read:
Code:<battle_description> <time_of_day>evening</time_of_day> </battle_description> <weather> <prevailing_wind x="15" y="-4"/> <lighting>naval</lighting> <environment_key>sc_temperate_coast_evening_clear_1_dry_summer</environment_key> </weather>
Updated Python Source Code (Revision 3):
Spoiler Alert, click show to read:Code:import struct, os, sys, re # For easy file reading and writing interactions def readLong(fhandle): return struct.unpack('l', fhandle.read(4))[0] def readShort(fhandle): return struct.unpack('h', fhandle.read(2))[0] def readByte(fhandle): return struct.unpack('B', fhandle.read(1))[0] def readBool(fhandle): val = fhandle.read(1) if val == "00": return False else: return True def writeLong(fhandle, value): fhandle.write(struct.pack('l',value)) def writeShort(fhandle, value): fhandle.write(struct.pack('h',value)) def writeByte(fhandle, value): fhandle.write(struct.pack('B',value)) def writeBool(fhandle, value): if value: fhandle.write('\x01') else: fhandle.write('\x00') def removeDir(path): # remove all files in a folder if not (os.path.isdir(path)): return True files = os.listdir(path) for x in files: fullpath=os.path.join(path, x) if os.path.isfile(fullpath): os.remove(fullpath) elif os.path.isdir(fullpath): removeDir(fullpath) os.rmdir(path) def parseArgs(args): pack = packFile('demo1.pack','unpacked') # create argument tree argtree = [] for arg in args[1:]: if arg.startswith('-'): argtree.append([arg,[]]) else: argtree[-1][1].append(arg) # wander the tree, top level always has hyphenated arguments for arg in argtree: # case 1: list if arg[0] == '-l': if len(arg[1]) > 0: for file in arg[1]: pack.printEyeCandy(str(file)) else: pack.printEyeCandy('./list.txt') # case 2: unpack elif arg[0] == '-u': for file in arg[1]: if file == 'all': for i in range(len(pack.files)): pack.exportFile(i) else: pack.exportFile(file) # case 3: unpack (regexp) elif arg[0] == '-ur': for file in arg[1]: pack.exportFile(file, True) # case 4: change pack elif arg[0] == '-p': for file in arg[1]: print() print('Changing pack to '+file) print() pack.newPack(file,pack.outputdir) # case 5: change output directory elif arg[0] == '-o': for file in arg[1]: print() print('Changing output directory to '+file) print() pack.changeOutputDir(file) class packFile: def __init__(self, path='', outputdir=None): self.handle = None if outputdir: removeDir(outputdir) self.newPack(path,outputdir) def newPack(self,path,outputdir=None): # safely open new pack if self.handle: self.handle.close() self.handle = None self.files = [] self.numFiles = 0 self.arr = 0 self.outputdir = outputdir self.defLength = 0 self.path = path self.readPackDefinition() def changeOutputDir(self,path): self.outputdir = path if self.outputdir != None: removeDir(self.outputdir) def packOpen(self): if not self.handle: self.handle = open(self.path,"rb") return self.handle def packClose(self): self.handle.close() def readPackDefinition(self): pack = self.packOpen() # skip empty bytes and stuff at the start pack.seek(16) self.defLength += 16 # read number of files self.numFiles = readLong(pack) self.defLength += 4 # read ?? self.arr = readLong(pack) self.defLength += 4 # store the offset of a certain file offset = 0 # read file metadata for i in range(self.numFiles): # read length of file length = readLong(pack) self.defLength += 4 # read file name char = '' filename = '' while char != b'\x00': char = pack.read(1) if (char != b'\x00'): filename += char.decode() self.defLength += 1 self.files.append((filename,length,offset)) offset += length def exportFile(self, arg, regexp = False): try: arg = int(arg) # option a: arg is an index list = [self.files[arg]] except: # option b: arg is a string if regexp: list = filter(lambda x: re.search(str(arg),x[0]),self.files) else: list = filter(lambda x: arg in x[0],self.files) for (path,length,offset) in list: print('Exporting '+path+', length: '+str(length)+', at offset: '+str(offset)) # create output directory dir = os.path.split(os.path.join(self.outputdir,path))[0] if not os.path.isdir(dir): os.makedirs(dir) output = open(os.path.join(self.outputdir,path),'wb') # open pack and go to offset pack = self.packOpen() pack.seek(self.defLength+offset) # copy content i = 0 # read MB-sized chunks as long as possible j = length//(2**20) while i < j: output.write(pack.read(2**20)) i+=1 i = 0 j = (length%(2**20))//(2**10) # read KB-sized chunks while i < j: output.write(pack.read(2**10)) i+=1 i = 0 j = length%(2**10) # read byte-sized chunks while i < j: output.write(pack.read(1)) i+=1 output.close() return True def printEyeCandy(self, outfile): output = open(outfile,'w') for (path,length,offset) in self.files: output.write(str(path)+'\r\n') output.close() # main parseArgs(sys.argv)