diff --git a/MW4COMPARE/DECOMPILING.md b/MW4COMPARE/DECOMPILING.md new file mode 100644 index 00000000..5db62e96 --- /dev/null +++ b/MW4COMPARE/DECOMPILING.md @@ -0,0 +1,1170 @@ +# Decompiling MW4 packed records back to source + +How to turn the compiled records inside a `.mw4` package back into the text source files the +content pipeline consumes -- what has been solved, how it was proved, and exactly where to +pick up. + +Written after a long investigation; the point of this document is that **none of it should +need rediscovering**. Everything below was measured, not inferred. + +> **Status -- all eight mech record types decompiled and verified** +> `.armature` 2,938 / 2,976 pages (65 chassis; the 38 residuals are data the packer discarded). +> `.subsystems` 7,579 / 7,585 keys (63 chassis; 6 residuals are a later source edit). +> `.data` 8,291 / 8,306 keys, `.damage` 6,605 / 6,605, `.contents` 7,480 / 7,480, +> `.instance` 896 / 896, `.torso` + `.engine` 1,280 / 1,280 -- all over our own 64 chassis. +> Every residual is classified in the section for its type; none is an unexplained decoder bug. + +--- + +## 1. Why this is needed + +A `.mw4` is **not** an archive of `Content/`. Measured across our own 102 packages +(51,382 records): + +| | count | +|---|---:| +| records byte-identical to a source file | 22,138 | +| records the packer generated or rewrote | 28,728 | + +The decisive example: our source `Content/Mechs/Atlas/atlas.data` is **4,891 bytes of text**. +The record packed under that name is a **12-byte binary stub** -- the real payload went into +`atlas.data{GameModel}` and `atlas.data{Element}`. + +So to port V4H's six new chassis (Champion, Dasher, Griffin, Jenner IIC, Marauder, +Thunderbolt) we cannot simply copy records into `Content/` and repack. The definition files +have to be reconstructed from the compiled form. + +### Which record types are which + +| verdict | extensions | +|---|---| +| **verbatim source** -- copy straight into `Content/` | `.tga` `.erf` `.mw4anim` `.bid` `.wav` `.bsp` `.material` `.abl` `.obb` `.ebf` `.bounds` `.animscript` `.fgd` `.tcf` `.mlr` `.d3f` `.gaf` `.script` `.h` `.abi` | +| **compiled** -- must be decompiled | `.data` `.instance` `.subsystems` `.damage` `.torso` `.engine` `.audio` `.video` `.lights` `.mw4`, and every `{hint}` `{handle}` `{gamemodel}` `{element}` `{footsteps}` `{zones}` `{nametable}` `[shadow]` `[joint_*]{armature}` `[joint_*]{sites}` record | + +Two qualified records are an exception -- they are real `.obb` files (`#BBO` magic, headers +byte-identical to ours) and only need renaming: + +``` +X.data{solidobb} -> X_skeleton_SOLID.obb (under Content/Mechs) +X.data{hierarchicalobb} -> X_skeleton.obb + -> X_SOLID.obb / X.obb elsewhere +``` + +`tools/split-source.py` already performs that classification and rename. + +--- + +## 2. The pipeline, and where the engine source lives + +``` +Content/.subsystems (text NotationFile, [Page] + Key=Value) + | + | MWTool::BuildResource mw4/Code/MW4/MWTool.cpp:142 + v + MWObject::CreateSubsystemStream mw4/Code/MW4/MWObject_Tool.cpp:1196 + | + | per [Page]: Tool::ConstructCreateMessage mw4/Libraries/Adept/Tool.cpp:680 + | -> script.classData->messageFactory + v + one of 51 per-class factories mw4/Code/MW4/*_Tool.cpp + | e.g. Armor_Tool.cpp, MWMover_Tool.cpp + v + a plain C struct (/Zp4) appended to the stream +``` + +The struct definitions are **in our own headers** -- `Armor__CreateMessage` in `Armor.hpp`, +`MWMover__CreateMessage` in `MWMover.hpp`, `Mover__CreateMessage` in `Adept/Mover.hpp`, +`Entity__CreateMessage` and `Replicator__CreateMessage` in `Adept/Entity.hpp` / +`Adept/Replicator.hpp`. There is no reverse-engineering to do; we have both the writer and +the reader. + +Useful entry points when adding a new type: + +| record type | writer | +|---|---| +| `.subsystems` | `MWObject::CreateSubsystemStream`, `MWObject_Tool.cpp:1196` | +| `.armature` | `MWObject::CreateArmatureStream`, `MWObject_Tool.cpp` (just above) | +| `.damage` | `MWObject::CreateDamageStream` -> `MWInternalDamageObject::ConstructMWInternalDamageObjectStream` | +| `[joint_*]{armature}` / `{sites}` | `MWMover__CreateMessage::ConstructCreateMessage`, `MWMover_Tool.cpp:78-84` | +| registration of packable types | `MWTool::IsRegisterable`, `MWTool.cpp:38` | + +--- + +## 3. Stream format + +Both `CreateSubsystemStream` and `CreateArmatureStream` write: + +``` +WORD span number of replicator IDs consumed +N x CreateMessage one per serialised [Page] +``` + +`span` equals the message count and, for `.subsystems`, the source `[Page]` count. +**Verified: all 65 chassis walk from offset 2 to exactly EOF with no slack.** + +### Verified message offsets + +``` +off 0 u32 messageLength <- walk the chain with this +off 4 u32 messageID +off 8 u32 priority +off 12 u32 messageFlags +off 16 u32 classID <- see the table in section 9 +off 20 u32 replicatorFlags +off 24 u32 replicatorID +off 28 12f localToParent rotation = cols 0..2, translation = col 3 of each row +off 76 u32 executionState +off 80 f32 initialAge +off 84 u32 dataListID ResourceID; the package record id is the HIGH word +off 88 u32 alignment +off 92 u32 nameID +--- Mover__CreateMessage --- +off 96 24B worldSpaceVelocity (Motion3D) +off 120 24B worldSpaceAcceleration (Motion3D) +--- MWMover__CreateMessage --- +off 144 u32 siteStreamResourceID +off 148 u32 armatureStreamResourceID +off 152 char jointName[128] +``` + +**How the layout was confirmed:** a joint message is exactly **280 bytes**, and +`152 + 128 = 280` accounts for every byte. Independently, `dataListID` in Atlas's `[Armor]` +message resolves to record **884**, which is exactly the `core.mw4` record id of +`subsystems\armor.data`. + +> `ResourceID` is packed -- the record id is the **high word**, so use `value >> 16`. An early +> byte-granular search found 884 at byte offset 86; that was a coincidental overlap with the +> real field at 84. + +### `{sites}` records + +A flat array, no message header (`MWMover_Tool.cpp` ~145: +`site_stream << rotation; << translation; << site_name;`): + +``` +repeat: + 3 x f32 YawPitchRoll, in RADIANS + 3 x f32 Point3D translation + u32 name length + bytes name + u8 NUL <- names are length-prefixed AND NUL-terminated +``` + +Forgetting the trailing NUL desynchronises everything after the first entry. That was the +one-byte bug that made the second site decode as garbage. + +--- + +## 4. The test harness -- why any of this is trustworthy + +We hold **both the source text and the compiled record for 65 chassis**. That gives a +closed-loop check: + +1. Decompile our own packed record. +2. Diff against the known source text. *(this is what is automated today)* +3. Recompile the regenerated text with the real VC6 packer and compare bytes. *(not yet + automated -- needs the Windows build machine)* + +Never add a record type without wiring it into step 2 first. + +```bash +python3 MW4COMPARE/tools/decompile/armature.py --verify +``` + +--- + +## 5. `.armature` -- solved + +### How it is stored + +The packer merges `.armature` into `.contents` via `!include=`, then for every +contents page that has `Child=` entries emits two records: + +| record | holds | +|---|---| +| `.contents[]{sites}` | children named `site_*` but **not** `site_eye*` | +| `.contents[]{armature}` | every other child (joints, and `site_eye*`), as CreateMessages carrying `jointName` + `localToParent` | + +Between them they hold every page of the original `.armature`. + +### Algorithm + +1. Read all `{sites}` records **first**, then all `{armature}` records. +2. `{sites}`: each entry is a page -- name, rotation (radians -> degrees), translation. Append + the name to its parent's child list. +3. `{armature}`: each message is a page -- `jointName`, translation from the matrix, rotation + `(0,0,0)` when the matrix is the identity, otherwise via `ypr_from_matrix`. +4. Emit depth-first, children before the joint that owns them. + +### Results + +| | | +|---|---| +| chassis rebuilt | **65** | +| pages compared | **2,976** | +| fully exact | **2,938** | +| child lists exact | **1,849 / 1,849** | +| residual | 38 | + +Sample, Atlas `[site_cageright]`: + +| | rotation | translation | +|---|---|---| +| decompiled | `0.000000 -90.000003 0.000000` | `-0.650000 -0.499999 1.150000` | +| source | `0.000000 -90.000000 0.000000` | `-0.650000 -0.499999 1.150000` | + +The 3e-6 is float32 rounding of a value the source stored as text. + +### The 38 residuals are packer-side losses, not decoder bugs + +The game reads the package, not the source, so **it never sees this data either**. Our +regenerated source is runtime-faithful. + +* **36** -- `site_lfoot` / `site_rfoot` rotation on 18 chassis. `MWMover_Tool.cpp` deliberately + writes those two sites to *both* streams (the comment reads *"Jerry this will get deleted + when you fix your foot problem"*); on those chassis both copies came out zeroed. The value + is ~90 degrees on every chassis where it did survive, so it can be restored by convention if ever + needed. +* **2** -- Victor's `.armature` declares `[site_lshellport]` and `[site_rshellport]` **twice**, + under different joints. `GetPage()` resolved both `{sites}` records to the first page. + Confirmed by reading both records directly: they hold identical transforms, so the second + page's values are genuinely absent from the package. + +### Three traps, all now handled + +1. **Order matters.** Read `{sites}` before `{armature}`. Alphabetically `{armature}` sorts + first, and if it wins, the foot sites get the zeroed matrix instead of the real angle. +2. **Do not double-count.** `site_lfoot` / `site_rfoot` appear in both streams; counting both + doubles them in the parent's child list. +3. **Key pages per occurrence, not by name.** Victor legitimately has two + `[site_lshellport]` pages and the Annihilator lists `site_lfoot` twice under one parent. A + `dict[name]` collapses them. + +### Output + +`.armature` files generated for all six new chassis into +`FS_Build_V4H_extracted/Content/Mechs/*/`: + +| chassis | pages | +|---|---:| +| champion | 64 | +| dasher | 58 | +| griffin | 67 | +| jenner2c | 53 | +| marauder | 64 | +| thunderbolt | 59 | + +--- + +## 6. Values are invertible -- worked example + +Atlas `[Armor]`, class 1201, message length 152. The last 44 bytes are nine `Scalar`s then +`m_armorType` and `m_internalType` (`Armor.hpp`): + +| | compiled | source | ratio | +|---|---:|---:|---:| +| LeftLeg | 68.80 | 2.15 t | 32.0000 | +| RightLeg | 68.80 | 2.15 t | 32.0000 | +| LeftArm | 62.40 | 1.95 t | 32.0000 | +| RightArm | 62.40 | 1.95 t | 32.0000 | +| LeftFrontTorso | 78.40 | 2.45 t | 32.0000 | +| RightFrontTorso | 78.40 | 2.45 t | 32.0000 | +| CenterFrontTorso | 70.40 | 2.20 t | 32.0000 | +| CenterRearTorso | 35.20 | 1.10 t | 32.0000 | +| Head | 9.60 | 0.30 t | 32.0000 | + +`Armor_Tool.cpp` multiplies tons by `m_pointsPerStandardTon` from `Subsystems\Armor.data`, +selected by `m_armorType`. So `tons = points / multiplier`, exactly -- but the multiplier +depends on armour type (Standard / FerroFiberus / Reactive / Reflective / Solarian), so read +it from the referenced `.data` rather than hardcoding 32. + +`.data` values survive too: Atlas `VehicleTonnage=28.0` sits at offset 108 of +`atlas.data{GameModel}` and `MaxVehicleTonnage=100.0` at 112, adjacent and in declaration +order. + +--- + +## 7. Recipe for adding the next record type + +1. Find the writer (see the table in section 2) and read what it emits, in order. +2. Read the message struct in the corresponding `*.hpp`. Fields are laid out in declaration + order after the base classes, `/Zp4`, no padding surprises so far. +3. Check the arithmetic: `sizeof(base) + own fields` must equal the observed `messageLength`. + This is the fastest way to confirm a layout -- it caught the 280-byte joint message and the + 108-byte Subsystem message immediately. +4. Write the decoder against `mw4msg.walk()`. +5. **Wire it into a verifier over all 65 chassis before trusting a single value.** +6. Expect residuals; classify each one as *decoder bug*, *packer-side loss*, or *source newer + than the package* before dismissing it. Every residual so far has been one of the last two, + but only because each was chased down. + +Two habits that paid off repeatedly: + +* **Derive enums empirically, then check them against the engine's text tables.** Walking the + aligned page/message pairs and tabulating `int -> source string` produced the + ExecutionState, locationID, ArmorType and InternalType maps in one pass, and simultaneously + proved the field offsets were right. +* **Read the factory, do not infer from the data.** `GroupIndex` looked like a plain integer + and decoded plausibly for most chassis; only `Weapon_Tool.cpp` reveals it is a bitmask that + can carry several groups. The same rule cracked the `.data` angle fields: sixteen of them are + declared plain `Stuff::Scalar`, and *only* `Mech_Tool.cpp` shows the writer multiplying them + by `Radians_Per_Degree`. +* **Never let a field map be discovered by the same values that verify it.** Searching for the + offset whose float equals the source value looks rigorous and reports 100%, but it is + circular: any field holding an identical value in every chassis has several valid-looking + candidates, and a verifier built on the same principle cannot tell them apart. Locate fields + by declaration order, anchored on the few that *are* unique, and keep value matching for + confirmation only. See section 8b -- this mistake silently mis-assigned a third of the + `.data` fields while reporting a perfect score. + +--- + +## 8. `.subsystems` -- solved + +### Results + +| | | +|---|---| +| chassis rebuilt | **63** | +| pages compared | **1,729** -- fully exact **1,723** | +| keys compared | **7,585** -- exact **7,579** | +| residual | 6 | + +The 6 residuals are Behemoth and Behemoth II `GroupIndex`, 3 each: commit `e45a67a8` +(`mfdsplit`) moved their Gauss rifles from weapon group 3 to group 1, and `core.mw4` has not +been repacked since. The decompiler reads 3, which is what the package genuinely holds. +battlemaster and battlemaster2c are skipped entirely for the same reason (41 source pages vs +32 packed messages). + +### Field map + +Beyond the Entity header, from `Subsystem.hpp`, `Weapon.hpp`, `Armor.hpp`, `AMS.hpp`: + +``` +off 96 i32 subsystemIndex +off 100 u8 locationID -> InternalLocation +off 104 i32 criticalHitsTaken -> CriticalHitsTaken + +Armor (len 152) +off 108 9xf32 armour POINTS -> tons = points / pointsPerTon +off 144 i32 m_armorType -> ArmorType +off 148 i32 m_internalType -> InternalType + +Engine (len 112, class 1073) +off 108 i32 m_engineUpgrades -> EngineUpgrades + +LAMS / AMS (len 112, class 1183) +off 108 i32 ammoCount -> AmmoCount (-1 means absent) + +SearchLight (len 236) +off 108 char[128] siteName -> Site + +Weapon (len 380, or 376 without the last field) +off 108 char[128] siteName -> Site +off 236 char[128] ejectSiteName -> EjectSite +off 364 i32 groupIndex -> GroupIndex (BITMASK - see below) +off 368 i32 ammoCount -> AmmoCount (-1 means absent) +off 372 i32 initialAmmoCount +off 376 i32 m_weaponFacing -> WeaponFacing (absent when len == 376) +``` + +Enum tables, derived empirically from the aligned pairs and cross-checked against the engine's +own text functions: + +| field | values | +|---|---| +| ExecutionState | 1 NeverExecuteState, 2 AlwaysExecuteState, 6 ActiveState | +| locationID | `InternalDamageObject` enum, `DamageObject.hpp:205`: 0 LeftLeg .. 7 Head, 8 Special1, 9 Special2 | +| ArmorType | 0 Standard, 1 FerroFiberus, 2 Reactive, 3 Reflective, 4 Solarian | +| InternalType | **separate 2-value enum** (`Armor.hpp:275`): 0 Standard, 1 EndoSteel | + +Armour is stored in **points**, not tons. Divide by the multiplier for the armour type, read +from `Content/Subsystems/Armor.data`: Standard 32, Ferro 38, Reactive 30, Reflective 30, +Solarian 60. Confirmed empirically -- every chassis inverts to the exact source tonnage. + +### Four traps + +1. **`GroupIndex` is a bitmask, not an index.** `Weapon_Tool.cpp` ~76 starts from + `DefaultWeaponGroupFlags` and ORs `1 << (n-1)` for *each* `GroupIndex=` line in the page. So + a stored 4 means group 3, and a page may legitimately list several groups. Emit one line per + set bit. +2. **`m_weaponFacing` is optional.** V4H packed champion/griffin/marauder at 376 bytes without + it -- matching their release note *"Any new mech will not have rear facing weapons"* -- + while dasher, jenner2c and thunderbolt have it at 380. Read both; our packer always emits + 380. +3. **`Model=` is relative to the mech's own directory** in the source but absolute in the + package. Strip the prefix using the **parent folder name**, not the file name: Black Hawk's + chassis files are `nova.*` inside `mechs/blackhawk/`. +4. **Page names are not stored.** The packer serialises order only, so names are regenerated + from `Model=` with a per-kind counter. They are labels; the runtime keys on order. + +### Was the index space disturbed by V4H's extra mechs? + +No, and this was checked rather than assumed: + +* V4H's `core.mw4` adds **no** entries under `subsystems/`, `weaponsubsystems/`, `weapons/`, + `tables/` or `effects/` -- all five sets are identical to ours. Every V4H-only entry is under + `mechs/`. +* For the 47 chassis both builds contain, the **`(classID, messageLength)` sequence is + byte-for-byte identical**. Class IDs did not shift. +* Only 19 distinct class IDs appear across both builds, and the only length variation is the + optional `m_weaponFacing` tail described above. + +--- + +## 8b. `.data{GameModel}` -- numeric field map solved + +The mech `.data{GameModel}` record is a flat **1636-byte** struct, not a CreateMessage +stream: `Mech__GameModel` inheriting `Vehicle__GameModel` <- `MWMover__GameModel` <- +`Mover__GameModel` <- `Entity__GameModel`. Every scalar member is a 4-byte float, `/Zp4`, laid +out in declaration order. + +### Results + + chassis : 64 + mapped keys : 74 + values compared : 4736 exact: 4736 wrong: 0 + +That covers every numeric `[GameData]` key. The 52 string, boolean, enum and resource-reference +keys are **not yet decoded** -- see section 11. + +### How the layout was established, and the wrong turn that came first + +The first attempt searched, for each source key, the set of offsets whose float equalled the +source value in all 64 chassis. It reported **4288/4288 exact** and was **wrong**. + +The flaw: 34 keys hold the same value in every single chassis -- `dampenWorldJoint` and +`fallAdjustmentSeconds` are both `0.5` everywhere -- so each had 2 to 6 equally valid +candidates, and a verifier built on value matching cannot distinguish them. Breaking the ties by +the order keys appear in the source file gave a plausible, self-consistent, perfect-scoring map +that put `TiltSpeed` at 672 (really `slopeDecel2`) and swapped `PercentageOfTurnToStartTilt` +with `PercentageOfSpeedToStartTilt`. On our 64 chassis it is undetectable. On a **new** mech +whose values differ it silently corrupts the field. + +The map is therefore built from **declaration order in the headers**, anchored on the fields +that value matching resolved to exactly one offset: + +| Block | Header | Base | Anchors that confirm it | +|---|---|---|---| +| `Vehicle__GameModel` | `mw4/Code/MW4/Vehicle.hpp` | 664 | `minSpeed` 692, `maxSpeed` 696, `acceleration` 720, `decceleration` 724, `reverseAccelerationMultiplier` 728 | +| `Mech__GameModel` | `mw4/Code/MW4/Mech.hpp` | 756 | `footReturnSeconds` 764, `dampenTorsoJoint` 800, `undampenRootJoint` 816, `undampenHipJoint` 824, `scaleInternalTiltDegree` 832 | + +Field *i* of a block sits at `base + 4*i`. Every anchor lands exactly, in both blocks, which is +what makes the arithmetic trustworthy. This verification can genuinely fail -- a mis-parsed +header shows up instantly as a whole column of wrong values. + +### Degrees vs radians + +Sixteen keys decoded to exactly `1/57.2958` of their source value. `Stuff::Radian` members +(`tiltSpeed`, `tiltDegree`, `topSpeedTurnRate`, `fullStopTurnRate`) explain four of them. The +other twelve -- the `torso`/`hip`/`rootHitSpring*` family -- are declared **plain +`Stuff::Scalar`**; the conversion is applied by the writer, e.g. `Mech_Tool.cpp:889`: + + model->torsoHitSpringMotionLimit = model->torsoHitSpringMotionLimit * Radians_Per_Degree; + +So the degree set is scraped out of `Mech_Tool.cpp` / `Vehicle_Tool.cpp` rather than inferred +from the declared type. Decoding divides by `Radians_Per_Degree` to return authored units. + +These twelve were among the "13 numeric keys with no consistent offset" under the value-matching +approach -- they had no matching offset precisely *because* they were scaled. + +### Four tail fields, and what they proved + +`MaxHeat`, `JumpJetTonnage`, `DamageNeededForCageEffect` and `AdvancedGyroTonnage` initially had +to be pinned by hand, because indexing scalars alone stops tracking the layout once non-scalar +members appear. They are now **derived**, and reproducing them was the test that validated the +whole layout engine. Two things had to be right: + +* `char leftJumpJetSiteName[MaxStringLength], rightJumpJetSiteName[MaxStringLength];` + (`MaxStringLength = 256`, `Entity.hpp:199`) -- 512 bytes that a member parser misses if it only + accepts literal array sizes. Their absence put `maxHeat` at 1060 instead of 1572. +* **`bool` is one byte, not four.** The six `m_canLoad*` flags occupy 6 bytes padded to 8, not 24. + Get this wrong and everything after them is off by exactly 16. + +With both fixed, the computed offsets are `maxHeat` 1572, `m_jumpJetTonnage` 1604, +`damageNeededForCageEffect` 1612, `m_advancedGyroTonnage` 1628 -- matching the four measured +values exactly. `datamap.layout()` now walks every member with `/Zp4` alignment +(`align = min(4, size)`) and no offset is hand-entered anywhere. + +### The engine contains its own decompiler + +`Entity__GameModel::SaveGameModel` (`Adept/Entity_Tool.cpp:864`) writes a `[GameData]` page back +out from a model. It does **not** hardcode key names -- it walks the class's +`gameModelAttributeTable` and calls `class_data->modelWriteToText(model, entry, &data)` per +attribute. So the authoritative key/member/type binding is the attribute table, populated by + + DIRECT_GAME_MODEL_ATTRIBUTE(class, AttributeName, memberField, type) // Entity.hpp:1315 + +343 registrations exist overall; **120 are in the mech chain** (Entity 5, Mover 7, MWMover 0, +Vehicle 21, Mech 87). The whole type vocabulary is: `Scalar` (201), `ResourceID` (54), `bool` +(21), `int` (20), `Point3D` (10), `const char *` (9), `Radian` (9), `Vector3D` (7), `RGBAColor` +(5), `UnitQuaternion` (2). Parse those macros and the name-to-member mapping stops being +guesswork -- which is what makes the remaining 52 keys tractable rather than a fishing exercise. + +### The struct chain, computed rather than assumed + +The real chain is `Entity -> Mover -> MWObject -> Vehicle -> Mech`. Two things had to be right +before it would compute: + +* `MWMover__GameModel` has no declaration -- it is `typedef Adept::Mover__GameModel` + (`MWMover.hpp:173`), so it contributes nothing. +* `Entity__GameModel` declares **no base class**, and wraps its members in `#if NSWIZZLE`. + `NSWIZZLE` is defined nowhere in the tree, so the `#else` branch is live -- and the two + branches **order their members differently**, so taking the wrong one silently shifts + everything. + +`chain_layout()` starts each block where the previous ended and checks itself against the two +independently measured anchors: + +| block | computed base | size | +|---|---|---| +| `Entity__GameModel` | 0 | 28 | +| `Mover__GameModel` | 28 | 52 | +| `MWObject__GameModel` | 80 | 584 | +| `Vehicle__GameModel` | **664** (measured 664) | 92 | +| `Mech__GameModel` | **756** (measured 756) | 878 | + +Mech ends at 1634, padding to the record's 1636. Both anchors are hit without being supplied, +which is the check that the member sizes and alignment rules are right; `chain_layout()` raises +if they ever disagree. + +### Results + + chassis : 64 + mapped keys : 114 + values compared : 5879 exact: 5879 wrong: 0 + not comparable : 1339 (enum / resource text) + +Reading is type-aware: floats, `int`, `bool` (one byte), `char[256]`, and `Point3D` / +`Vector3D` / `RGBAColor` / `UnitQuaternion` vectors. `VehicleBattleValue` was the field that +forced this -- as a float it decoded to `7.00649e-45`, the float reinterpretation of `int 5`. + +### Dependencies remaining to finish `.data` + +All resolved -- see section 8c. + +--- + +## 8c. `.data` -- solved + +### Results + + chassis : 64 + keys compared : 8306 exact: 8291 wrong: 14 missing: 1 + deliberately omitted: 256 (unreadable by the engine) + +`python3 verify_roundtrip.py` regenerates a whole `.data` from the compiled records for every +chassis and diffs it against the authored source. The comparison is semantic, not byte-exact: +NotationFile is order-independent, and **Windows path lookup is case-insensitive** -- our own +tree writes both `mechs\atlas_destroyed\...` and `Mechs\Atlas_Destroyed\...` for the same key, +which settles that no canonical-case recovery is needed. + +The 15 residuals are understood and none is a decoder defect: + +* **`SolidOBB` / `HierarchicalOBB`, 7 chassis (14 values).** These name a *source-side* `.obb` + file, and the packer stores only the qualified resource (`x.data{SolidOBB}`), not the filename. + Seven chassis used abbreviations (`Gla_`, `Hau_`, `Lon_`, `Owe_`, `pum_`, `MadCat2_`) that are + not derivable. Irrelevant in practice: we ship the `.obb` for a new chassis, so we choose the + name, and the key only has to match the file next to it. +* **`VehicleBattleValue`, 1 chassis.** Authored by 1 of 64. Emitting it everywhere would add a + key to 63 files that never had one, so rare keys are suppressed instead. + +### Four keys are unrecoverable, and it does not matter + +`BattleDamageRatio`, `BattleKillBonus`, `DragoonValue` and `VehicleTradeValue` appear in all 64 +sources but are read by **nothing** in the engine -- no attribute registration, no factory, no +runtime reference. They never enter the package. They are authoring metadata; the decompiler +omits them deliberately rather than inventing values. + +### Value sources + +| kind | how | count | +|---|---|---| +| struct member | typed read via `chain_layout()` | 117 keys, 6071/6071 exact | +| `ResourceID` | record id is the HIGH word, `>> 16`, then the manifest | 1083/1083 exact | +| symbolic constant | int reversed via `constants.py` | 256/256 exact | +| factory-written | handled explicitly in `data.py` | the rest | + +Symbolic tables: `M_*` and `Tech_*` from `Content/ShellScripts/MechLabHeaders.h`, `IDS_*` from +`Content/Defines/MissionLang.defines`, `MoveTypeFlag` from the anonymous enum at +`MWObject.hpp:136` (declaration order, *not* the order of the `stricmp` chain that reads it). +Values are not unique -- `IDS_FIRSTSKIN` and `IDS_WOLFHOUND` are both 501 -- so candidates are +kept as a list and disambiguated by chassis name. + +### Keys the attribute table does not cover + +`SaveGameModel` writes these outside the attribute table, so each needed its own factory read: + +* `AnimationScript` -> `animScriptName`, a `char[256]` member; reproduces 64/64 exactly. +* `HeatManager` -> `heatManagerResource`, `FootEffectsFile` -> `footFallEffectsTable` (aliases: + the source key does not match the member name). +* `Shadow` -> byte-identical in all 89 mech `.data` files, emitted as a constant block. +* `CraterName` -> `m_craterID` holds `MString::GetHashValue` (`DeathEntity_Tool.cpp:34`), which + is **one-way**. All 64 chassis store the same hash, so the single authored value `crater01` is + recovered by constancy, not by inversion. +* `Class` and the four lighting flags -> constant across all 64. +* `DefaultFootStepTexture` / `FootStepTexture` -> the `{FootSteps}` record: `int material` + (-1 = default), `int length` **not counting the terminator**, the characters, a NUL, then a + one-byte isDefault flag. Material indices are the enum at `Adept.hpp:212`, where `NoMaterial` + is 0 (so 5 = BrownDirt, 12 = Snow). + +Booleans are spelled inconsistently by key -- `Collider` and `CanBeShot` use `true`/`false`, the +`CanLoad*` flags use `Yes`/`No` -- so the spelling is learned per key from the corpus. + +### A parsing bug that silently truncated every source + +`Shadow={...}` contains a line reading `[shadow]`. A page scan that stops at the next `[` therefore +stopped **inside** the Shadow block, discarding every key after it -- `SplashDamageAmount`, +`SplashDamageRadius`, `SplashHeatAmount` and more -- from the comparison corpus. Nothing failed; +the harness simply never saw them, and reported a clean pass on a subset. Braced blocks are now +hidden before the page split, and both CR **and** LF must be replaced while doing so, because +`splitlines()` also splits on a bare CR. + +Worth generalising: **a verifier that silently narrows its own input reports success.** The keys +appeared only when an unrelated fix made the parser see the rest of the file. + +### V4H stores mech ids from an older roster + +V4H's `MechLabHeaders.h` appends the six new mechs at 65-70 and agrees with ours on 0-64 +(`M_Atlas = 6` in both). But the ids **stored in their compiled records** disagree: their Atlas +holds 5, and 64 of 65 shared chassis are off by one, with outliers (`Behemoth` and `Behemoth2` +both hold 1). Their packages were built against an earlier roster, so the stored integer is +stale. + +This does not affect anything else -- V4H's Atlas decompiles with the correct `AnimationScript`, +`DeathEntityResource`, `VehicleTonnage` and `MaxSpeed`, which is how the pipeline was cleared of +suspicion. It affects only `MechID` and `NameIndex`. + +Since both keys are authored as **symbols** rather than integers, `--retarget-ids` emits +`$(M_)` / `$(IDS_)` and lets the build resolve them, instead of reversing a +stale int into some other mech's name. It is opt-in: our own tree has legitimate aliases +(`blackhawk` is `nova`) that the rule would otherwise rewrite. + +--- + +## 8d. `.damage` -- solved + +### Results + + chassis : 64 + pages compared : 1188 + keys compared : 6605 exact: 6605 wrong: 0 + +`python3 verify_damage.py` regenerates each `.damage` and compares page names, page order, key +sets and values against the authored source. + +### Format + +Unlike everything above this is **not** a CreateMessage stream and has no index: it is a bare +concatenation of variable-length objects. Parsing means walking forward, reading a `classID`, and +letting it decide what follows. `MWObject::CreateDamageStream` (`MWObject_Tool.cpp:1149`) iterates +the source pages in order and dispatches on whether a page carries a `DamageZone` entry. + +Armour page, classID **468** (`DamageObject::ConstructDamageObjectStream`, `DamageObject.cpp:157`): + + classID, baseArmorValue, currentArmorValue, scaleSplashDamage, + damageObjectName (MString), internalDamageZoneID, armorZone, damageLevel, + armorType, maxArmorValue, attachedToZone + +Internal page, classID **1162** (`DamageObject.cpp:931` plus the MW4 subclass at +`MWDamageObject.cpp:88`): + + classID, baseInternalDamage, currentInternalDamage, parentEntityName (MString), + damageMode, damageZone, damagePropagationZone, internalType, attachedTo, + damageEffects[count][resourceID, armorPercent], + missileSlots, projectileSlots, beamSlots, omniSlots + +`MString` is an int length **not counting the terminator**, the characters, then a NUL -- the same +encoding as the `{FootSteps}` stream in section 8c. + +### Three traps + +* **The armour page stores its own name; the internal page does not.** Internal names are rebuilt + as `Internal` from the `damageZone` that *is* stored. All 89 mech `.damage` files follow + that convention exactly (`LeftArmInternal`, `CenterTorsoInternal`, `Special1Internal`). +* **`ArmorZone` and `InternalZone` are different enums.** `ArmorZone` (`DamageObject.hpp:363`) has + `CenterRearTorso` at 7 and `Head` at 8; `InternalZone` (`DamageObject.hpp:204`) has `Head` at 7 + and no rear-torso entry. Conflating them silently mislabels head and torso zones -- the values + differ by one exactly where it is least obvious. +* **Defaults are indistinguishable from explicit values.** The writer defaults + `max_armor_value = base_armor_value` and `damage_mode = GeneralDamageMode`. No source writes + `GeneralDamageMode` explicitly, so a zero there is safely emitted as "omitted". `MaxArmorValue` + is *not* safe: 32 pages omit it while **51 pages state it explicitly equal to + `BaseArmorValue`**, and both produce identical bytes. The distinction is unrecoverable, so the + decompiler always writes it and the verifier accepts either form. + +--- + +## 8e. `.contents` -- solved + +### Results + + chassis : 64 + pages compared : 3740 + keys compared : 7480 exact: 7480 wrong: 0 + +`.contents` is the thin half of the pair section 5 already handles. It `!include`s +`.armature` and gives every joint and site exactly two entries: + + [joint_torso] + Model=basic.data + ExecutionState=AlwaysExecuteState + +Nothing new had to be located. Both values ride in the **same CreateMessages** `armature.py` +walks -- the `.armature` source contributes a page's geometry, the `.contents` source +contributes its Model and ExecutionState, and the packer merges them into one message per page. +`Model` is `dataListID` (record id in the high word), `ExecutionState` the enum at offset 76. + +### Three things to know + +* **`Model=` is written relative to the mech folder.** The manifest returns + `mechs\annihilator\armaturedata\ann_rfoot.data`; the source says + `armaturedata\ann_rfoot.data`. Strip the `mechs\\` prefix. This alone accounted for + all 1124 initial mismatches. +* **Site pages carry no Model in the package.** `{sites}` records store only name, rotation and + translation. They do not need to: all **2926** site pages across the 89 mech `.contents` files + carry the identical pair `basic.data` / `AlwaysExecuteState`, so they are emitted as constants + rather than guessed. +* **Which sites had a `.contents` page is not recoverable.** `.contents` declares a *subset* of + the sites in `.armature` -- `site_eject2` appears in 89 `.armature` files but only 8 + `.contents`. Emitting every site over-emits ~2.4 pages per chassis (152 total); emitting only + the `{armature}` messages would lose ~21 per chassis. The former is far closer and harmless, + since the added pair is the same default every other site carries. + +12 site pages across all 64 chassis appear in the source but in no record at all, and are lost. + +### Name consistency is on us + +The package stores neither the `.obb` filenames nor the `.armature` filename, so the decompiler +chooses them -- and they must agree with the files actually shipped. V4H's Jenner IIC exposed +this: its records use the stem `jenner_2c` while its folder is `jenner2c`, so a chassis-derived +name produced `SolidOBB=jenner2c_Skeleton_SOLID.obb` next to a file called +`jenner_2c_skeleton_SOLID.obb`, and a `!include=jenner_2c.armature` next to `jenner2c.armature`. +`data.py` now reads the `.obb` names from the output directory rather than inventing them, and +the generated set is checked so that every referenced file exists. + +--- + +## 8f. `.torso` and `.engine` -- solved + +### Results + + torso files : 64 + engine files : 64 + keys compared : 1280 exact: 1279 wrong: 1 + +Both are single-page `[GameData]` subsystem models stored as flat structs, so they reuse +`datamap.chain_layout()` with their own chains. Both compute to **exactly** the record size with +no anchoring, which is the check that the member list and alignment are right: + +| model | chain | computed | record | +|---|---|---|---| +| Torso | `Entity -> Subsystem -> Torso` | 1608 | 1608 | +| Engine | `Entity -> Subsystem -> Engine` | 64 | 64 | + +Sources author the numbers as `$(SYMBOL)` macros from an `!include`d defines file -- note these +use **`!NAME=value`** syntax, not `#define` like `MissionLang.defines` -- and the record keeps +only the resolved float, so the symbol is restored by reverse lookup where one matches. + +* The five Torso angles are declared plain `Stuff::Scalar` but `Torso_Tool.cpp` multiplies them + by `Radians_Per_Degree`, exactly like the Mech spring fields in section 8b. +* `TotalCritLocations` is read by **nothing**: only the 3DS Max exporter writes it, and the + factory reads `TotalSlotsTaken`, which no source sets. The record holds the default 1 while + every source says 2. It is emitted as the constant it always is. +* The engine's `Class` is `Mechwarrior4::Engine` -- lowercase 'w', in all 89 sources. Preserve + the typo. + +### A latent typo, and why the fix was to write the value down + +`cauldronborn.torso` said `TwistRadius=$(OBSTUSE_TRADIUS)` -- a typo for `OBTUSE`, defined +nowhere, so `Torso_Tool.cpp:137` fell back to its default `100.0f`. The source therefore said +one thing and the game did another. + +**100 is the intended value**, confirmed by the project owner, so the fix was *not* to correct +the symbol to `$(OBTUSE_TRADIUS)` (which would have silently changed the Cauldron Born's twist +radius from 100 to 140). It is now the literal `TwistRadius=100` -- behaviour-preserving, and +matching the 25 other `.torso` files that write a literal rather than a symbol. There is no +symbol for 100 in `MechTorso.defines`; inventing one for a single user was not worth it. + +`verify_smallmodel.py` reports **1280/1280** with the source and package now agreeing, which is +itself the evidence that 100 was the operative value all along. + +A sweep of every `$(SYMBOL)` in all mech sources against all defines found no other undefined +macro. The general lesson: an undefined macro does not fail loudly here -- the factory quietly +substitutes a default, so the source can drift from the shipped behaviour indefinitely. Check +what the package actually holds before "fixing" a symbol. + +--- + +## 8g. `.instance` -- solved + +### Results + + chassis : 64 + page names : 64/64 match + keys compared : 896 exact: 896 wrong: 0 + +One page named after the chassis, holding the model/armature/subsystem/damage references and the +mechlab bar ratings. The record is a single `Mech__CreateMessage`, so the layout comes from the +**CreateMessage** chain rather than the GameModel one: + + Replicator -> Entity -> Mover -> MWMover -> MWObject -> Vehicle -> Mech + +`chain_layout(..., start=16)` computes it; the 16 is the `Connection__Message` header +(messageLength, priority, flags) which sits in front and is declared in none of these classes. +The result ends at 341 and pads to exactly the 344-byte record, and **every offset established +independently back in section 3 lands on the nose** -- classID 16, replicatorID 24, +localToParent 28, dataListID 84, alignment 88, jointName 152. That is six independent +confirmations from a layout that was never told about any of them. + +Two parser gaps had to be closed first, both fields typed with names the member regex did not +know: `Stuff::RegisteredClass::ClassID` and `ReplicatorID` in the Replicator base, and +`Entity__ExecutionStateEngine::FactoryRequest` and `ObjectID` in Entity. Missing them shifted +everything after offset 76 by 8 bytes **while still producing a plausible-looking table** -- the +known offsets were what caught it. + +References are written relative to the mech folder, same as `Model=` in section 8e. +`CollideeType` and `CollisionMask` are not emitted: they appear in 5 of 64 sources, are +uniformly `Zone` / `-1`, 11 of their occurrences are commented out, and no factory reads them -- +only `TCTb`, `InterestBSP` and the old `MW4GameEd` ever write them. + +--- + +## 9. Reference tables and package baseline + +### classID map + +The message chain aligns 1:1 with source pages, so `classID` can be mapped by walking both in +lockstep. Derived from the 61 chassis that aligned before the malformed-header repair below +(63 align now): + +| classID | msgLen | count | class (from the page's `Model=`) | +|---:|---:|---:|---| +| 1181 | 108 | 842 | `subsystems\heatsinksubsystem.data` | +| 1106 | 380 | 210 | laser / pulse-laser weapon subsystems | +| 1134 | 380 | 98 | machine gun, ultra AC | +| 1073 | 112 | 61 | `.engine` | +| 1201 | 152 | 61 | `subsystems\armor.data` | +| 1223 | 108 | 61 | `subsystems\advancedgyrosubsystem.data` | +| 1157 | 108 | 61 | `subsystems\sensorsubsystem.data` | +| 1130 | 108 | 61 | `.torso` | +| 1193 | 236 | 61 | `subsystems\searchlightsubsystem.data` | +| 1142 | 380 | 60 | LRM weapon subsystems | +| 1100 | 108 | 23 | `subsystems\jumpjetsubsystem.data` | +| 1143 | 380 | 23 | streak SRM weapon subsystems | +| 1140 | 380 | 18 | SRM weapon subsystems | +| 1158 | 108 | 11 | `subsystems\ecmsubsystem.data` | +| 1183 | 112 | 10 | `subsystems\lams.data` | +| 1159 | 108 | 7 | `subsystems\beaglesubsystem.data` | +| 1155 | 380 | 3 | `narcbeacon.data` | + +Only ~17 distinct classes actually occur in mech `.subsystems`, not the full 51 -- and all +weapons share a 380-byte message, so one weapon decoder covers most of them. + +`Model=` is recoverable from `dataListID >> 16` looked up in the package manifest, so the +class table is a cross-check rather than the primary mechanism. + +### Alignment: 63 of 65 chassis match 1:1 + +Only two chassis genuinely differ: + +| chassis | messages | source pages | meaning | +|---|---:|---:|---| +| battlemaster | 32 | 41 | source is newer than the package | +| battlemaster2c | 32 | 41 | same | + +`battlemaster.subsystems` and `battlemaster2c.subsystems` were rewritten with full IS stock +loadouts on branch `mfdsplit` (commit `e45a67a8`), which is far newer than the packed +`core.mw4`. Do not use those two for a `.subsystems` round-trip test until `core.mw4` is +repacked. + +Two other chassis appeared to mismatch during the investigation and did not: +`hellspawn` and `sunder` each had one malformed page header -- `[HeatSink10` and +`[HeatSink16`, **missing the closing bracket**. The engine's NotationFile parser accepts a +header with no closing `]` and still creates the page, which is why the package had one more +message than a strict `\[[^\]]+\]` regex counted. Both were repaired (one byte each, CRLF +preserved); they were the only two malformed headers in the entire mech content set. + +> **Lesson for the `.subsystems` decoder:** the runtime parser is more lenient than an obvious +> regex. Parse page headers as "line starts with `[`", not "line matches `\[...\]`", or the +> counts will silently disagree. + +### Which commit the packages correspond to + +**This is the single most important thing to check before trusting any comparison.** The +extracted trees reflect the *packages*, not `Content/` at HEAD: + +| package | last committed | note | +|---|---|---| +| `Resource/core.mw4` | `2b8ca921` (initial mirror) | **never repacked** since the original import | +| `Resource/textures.mw4` | `2b8ca921` | never repacked | +| `Resource/props.mw4` | `2f176310`, working tree current as of `8bfaf9b9` | repacked twice | + +So any `Content/` edit made after those commits is invisible to `FS_Ours_extracted`. Before +concluding "the decompiler lost something", check whether the source simply moved on. Run +`git log --oneline -- ` and compare against the package's commit above. + +--- + +## 10. Tools + +Under `MW4COMPARE/tools/decompile/`: + +| file | purpose | +|---|---| +| `mw4msg.py` | CreateMessage stream reader. All verified offsets are documented in its docstring -- start here. | +| `armature.py` | `.armature` decompiler. `--verify` runs the harness; ` -o out.armature` writes one mech. | +| `verify_armature.py` | 65-chassis harness. Matches pages as a multiset of `(name, rotation, translation)` so duplicate page names are handled. | +| `subsystems.py` | `.subsystems` decompiler. `--verify` runs the harness; ` -o out.subsystems` writes one mech. | +| `verify_subsystems.py` | 63-chassis harness. Compares key/value pairs page by page, ignoring page names, and treats an explicit `=0` as equivalent to an omitted key. | +| `destroyed.py` | `*_destroyed` `.data` + `.video` generator. `--verify` regenerates all 89 of ours and diffs. | + +```bash +cd MW4COMPARE/tools/decompile +python3 armature.py --verify +python3 subsystems.py --verify +python3 verify_data.py # .data{GameModel} typed field map, 64 chassis +python3 verify_roundtrip.py # whole .data regenerated and diffed, 64 chassis +python3 verify_damage.py # whole .damage regenerated and diffed, 64 chassis +python3 verify_contents.py # whole .contents regenerated and diffed, 64 chassis +python3 verify_smallmodel.py # .torso and .engine regenerated and diffed, 64 chassis +python3 verify_instance.py # whole .instance regenerated and diffed, 64 chassis +python3 datamap.py # print the field map itself, offset order +python3 constants.py # print the symbolic tables + +V=/home/rich/Repositories/FS_Build_V4H_extracted +python3 armature.py "$V/_compiled/Content/Mechs/champion" -o "$V/Content/Mechs/champion/champion.armature" +python3 subsystems.py "$V/_compiled/Content/Mechs/champion/champion.subsystems" "$V/_manifest.tsv" \ + -o "$V/Content/Mechs/champion/champion.subsystems" +python3 data.py "$V/_compiled/Content/Mechs/champion" -m "$V/_manifest.tsv" \ + --retarget-ids -o "$V/Content/Mechs/champion/champion.data" +python3 damage.py "$V/_compiled/Content/Mechs/champion" -m "$V/_manifest.tsv" \ + -o "$V/Content/Mechs/champion/champion.damage" +python3 contents.py "$V/_compiled/Content/Mechs/champion" -m "$V/_manifest.tsv" \ + -o "$V/Content/Mechs/champion/champion.contents" +python3 instance.py "$V/_compiled/Content/Mechs/champion" -m "$V/_manifest.tsv" \ + -o "$V/Content/Mechs/champion/champion.instance" +python3 smallmodel.py torso "$V/_compiled/Content/Mechs/champion" -o "$V/Content/Mechs/champion/champion.torso" +python3 smallmodel.py engine "$V/_compiled/Content/Mechs/champion" -o "$V/Content/Mechs/champion/champion.engine" +``` + +`datamap.py` derives the layout from the headers plus the tool factories and exposes +`chain_layout()`, `build() -> (corpus, {sourceKey: (offset, type, size, isAngle)})` and +`read(blob, offset, type, size, isAngle)`. `data.py` is the `.data` decompiler; +`--retarget-ids` is required when decompiling from a foreign tree whose roster differs. + +Supporting tools one level up (`MW4COMPARE/tools/`): `mw4db.py` (package reader), +`extract-all.py`, `prune-identical.py`, `classify-survivors.py`, `restructure.py`, +`split-source.py`. See `MW4COMPARE/README.md`. + +### Input trees + +| path | what | +|---|---| +| `/home/rich/Repositories/FS_Ours_extracted` | our own packages, unpacked, **per-package** layout -- the baseline for every comparison. Regenerate in ~4 min. | +| `/home/rich/Repositories/FS_Build_V4H_extracted` | V4H, pruned to differences and reshaped: `Content/` = repackable source, `_compiled/` = records to decompile | + +--- + +## 11. Progress: what exists for the six new chassis + +Written to `FS_Build_V4H_extracted/Content/Mechs//`. Everything below is real source, +ready to drop into `Gameleap/mw4/Content/Mechs/`. + +| chassis | `.erf` etc | `.armature` | `.subsystems` | `.data` | `.damage` | `.contents` | `.instance` | `.torso` | `.engine` | +|---|---|---|---|---|---|---|---|---|---| +| champion | yes | 64 pages | 22 pages | 137 keys | 21 pages | 66 pages | yes | yes | yes | +| dasher | yes | 58 | 9 | 137 | 17 | 60 | yes | yes | yes | +| griffin | yes | 67 | 17 | 137 | 19 | 65 | yes | yes | yes | +| jenner2c | yes | 53 | 12 | 137 | 17 | 55 | yes | yes | yes | +| marauder | yes | 64 | 23 | 137 | 21 | 66 | yes | yes | yes | +| thunderbolt | yes | 59 | 30 | 137 | 19 | 61 | yes | yes | yes | + +**All eight source files now exist for all six chassis**, and every internal reference +(`Model=`, `Armature=`, `Subsystems=`, `DamageObjects=`, `SolidOBB=`, `HierarchicalOBB=`, +`!include=`) resolves to a file that is actually present. + +`.data` files were generated with `--retarget-ids`, so each carries `$(M_)` and +`$(IDS_)`. **Those two symbols do not exist in our tree yet** -- they must be added to +`Content/ShellScripts/MechLabHeaders.h` and `Content/Defines/MissionLang.defines` before the +mech will build. That is part of the normal registration chain in `ADDING-A-MECH.md`. + +Every folder carries a `STATUS` file listing what is present and what is missing, per the +staging convention below. Delete it in the same commit that adds the chassis to `core.build`. + +### Decisions on the two chassis we already had (2026-08-08) + +Two of the six exist in our tree already, unregistered. Both were compared in full; **V4H's +version wins in both cases**, for different reasons. + +**Jenner IIC -- use V4H's, scrap ours.** Zero art is shared (prefix `j2c_` vs `jec_`, 0 of 27 +comparable parts byte-identical), so these are unrelated models rather than two builds of one. +Ours is unfinished scaffolding: `.subsystems` has **no weapons at all**, every armour value is +the placeholder `0.1`, `EngineUpgrades=0`, it uses `Mech_Foot_Large` effects on a 35-ton light +mech, and its `.data` references `content\mechs\jenner2c\jenner2c.animscript` -- **a file that +exists nowhere in our tree**. V4H's has a full loadout (MG/LRM5/SRM4/flare, jump jet, 2 heat +sinks), the animscript, and 154 animation files. The one thing ours had was higher-detail art +(1065 KB against V4H's 250 KB, where shipped light mechs run 390-490 KB) -- 2.5x the roster +norm, and plausibly why it was never finished. + +**Dasher -- use V4H's; keep `Dasher_DNU/` in the repo as reference, do not build it.** Worth +recording that **the DNU is not broken**. It is complete and self-consistent, and richer in +places: its own 38 KB `dasher.animscript` (V4H's package has none and borrows Dragon's), real +jump-jet sites, `LEGJUMPMOVETYPE`, and `OmniSlots=8` arms modelling it as a true OmniMech. All +29 shared `.erf` files are **byte-identical**, so the art is interchangeable if the DNU data is +ever revived. + +Why it was marked DNU could not be determined. Ruled out by measurement: missing files (it has +the same eight core files as a shipped mech), stub `_dam` geometry (normal -- the shipped +Annihilator shows the same pattern), a 78-byte `_SOLID.obb` (every mech's is 78 bytes), +anomalous top speed (50.0 is the common roster value), cross-referenced assets (that is the +*other* DNU mech, Grimreaper, whose destroyed `.video` points at `grizzly_destroyed.erf`), and +dangling references (none). Git cannot help -- the repo is a single initial-mirror commit, so +the rename predates version control. The strongest remaining hypothesis is a balance decision +rather than a defect: a freely configurable 8-slot OmniMech on a 20-ton chassis at the roster's +top speed. That is inference, not evidence. + +### All six carry a movement-type inconsistency + +Found while comparing the Jenner, but it is not confined to it: + +| V4H chassis | MoveTypeFlag | JumpJetTonnage | JumpJet subsystem | LeftJumpJetSiteName | +|---|---|---|---|---| +| champion | LEGMOVETYPE | 4 | -- | site_lujetport | +| dasher | LEGMOVETYPE | 2 | -- | `;` | +| griffin | LEGMOVETYPE | 3 | **yes** | site_lujetport | +| jenner2c | LEGMOVETYPE | 2 | **yes** | site_lujetport | +| marauder | LEGMOVETYPE | 4 | -- | `;` | +| thunderbolt | LEGMOVETYPE | 4 | -- | site_lujetport | + +**All six cannot jump**, yet all six carry jump-jet tonnage, four name a real jump-jet site, and +two have a `JumpJetSubsystem` equipped. Our own roster is 84 `LEGJUMPMOVETYPE` against 5 +`LEGMOVETYPE`, and those 5 have *empty* jump-jet site names -- internally consistent. V4H's are +not. This is decompiled faithfully; the inconsistency is in their source data. Decide per +chassis whether to set `LEGJUMPMOVETYPE` or strip the jump-jet gear before building. It is step +3 in every `STATUS` file. + +### Staging convention: land the files, mark them, leave `core.build` alone + +Decoded output goes into `Gameleap/mw4/Content/Mechs//` as it is produced, **before** +the chassis is complete. That is safe, and it was checked rather than assumed: + +* **Packing is manifest-driven.** Every mech file is an explicit `data=` / `armature=` / + `subsystems=` / `damage=` / `instance=` line in `Content/core.build` (528 of them). The only + `directory=` sweeps in the whole tree are `content\shellscripts*`, `content\force` and + `content\ablscripts`. An unreferenced folder is never visited by the packer, so `core.mw4` is + unchanged and `build-resources.ps1` output stays byte-identical. +* **The editor never enumerates `Content\Mechs`.** MW4Ed2 STOPs fatally on the first content + defect, so this was the obvious risk. Its `FindFirstFile` calls cover `Content\Maps`, + `Content\Missions`, `Content\Skies`, `Content\ABLScripts`, `Content\Audio` and + `Resource\UserMissions` only -- mechs come from the compiled package, not a directory scan. +* **The game deploy drops them.** `deploy-mw4.ps1` keeps only `Content\shellscripts\files` and + drops `Content\*`, so `MW4\` is unaffected. +* Mech IDs are positional indices in code arrays and tables, not derived from directory + listings, so extra folders shift nothing. + +`core.build` is the switch. Files stay inert right up until those manifest lines are added, at +which point any defect becomes a fatal STOP at resource-build time. **Add the manifest lines only +when a chassis is fully decoded and verified**, one chassis at a time. + +Because the folders will look populated while still being incomplete, every incomplete chassis +carries a plain-text **`STATUS`** file naming exactly what is missing, so neither a future +session nor a human mistakes it for ready: + + Gameleap/mw4/Content/Mechs/champion/STATUS + + INCOMPLETE - do not add to Content/core.build yet. + have: .erf/.mw4anim/.obb source, .armature (64 pages), .subsystems (22 pages) + missing: .data (numeric keys decode; 52 string/bool/enum/resource keys do not), + .damage, .contents, .instance, .torso, .engine + see MW4COMPARE/DECOMPILING.md section 11 + +Delete the `STATUS` file in the same commit that adds the chassis to `core.build`. Its presence +is the marker for "staged, not wired up"; its absence means the chassis is live. + +Note the inverse risk that motivates all of this: a **wrong** decoded file is more dangerous than +a missing one, because it packs and loads and then misbehaves subtly in game. That is the +argument for finishing a record type properly rather than emitting a best-effort file. + +"`.erf` etc" is the verbatim-source set already separated by `split-source.py`: geometry +`.erf`, `.mw4anim` animation, `.tga` textures, `.obb` (renamed from `{solidobb}` / +`{hierarchicalobb}`), `.animscript`, `runninglights.erf`, `armaturedata/`, `armaturevideo/`. + +### The `*_destroyed` variants -- complete + +Each destroyed variant is four files, and all four now exist: + +| folder | `.erf` | `_SOLID.obb` | `.data` | `.video` | +|---|---|---|---|---| +| champion_destroyed | package | package | generated | generated | +| griffin_destroyed | package | package | generated | generated | +| jenner2c_destroyed | package | package | generated | generated | +| marauder_destroyed | package | package | generated | generated | +| thunderbolt_destroyed | package | package | generated | generated | +| **Dasher_destroyed** | **already ours** | **already ours** | **already ours** | **already ours** | + +**Dasher_destroyed has no folder in the differences-only tree and needs none.** Both of its +packaged files were byte-identical to `Content/Mechs/Dasher_destroyed/` in our repo and were +pruned - they were in fact the only two files in the entire prune that matched via our source +tree rather than via our packages. + +`.data` and `.video` are compiled away in the package (a 12-byte stub plus +`{Element}`/`{GameModel}`, and a binary element tree with embedded `#FRE`/`#RLM` blobs), so +`destroyed.py` regenerates them from a template. That is sound because the template is +invariant: across all 89 destroyed variants in our own tree, `Class`, `OBBCollides`, +`Collider`, `CanBeShot`, `CanBeWalkedOn`, `VertexLighting`, `FaceLighting`, `LookupLighting`, +`LightMapLighting` and `CraterName` are identical in every one. Only the three filename +references vary, and those are read from the files actually present rather than guessed. + +``` +python3 destroyed.py --verify + destroyed variants checked : 89 + .data equivalent : 89 (byte-exact 44, +45 case / trailing-blank-line only) + .video equivalent : 89 (byte-exact 62, +27 case / trailing-blank-line only) +``` + +The non-byte-exact cases are cosmetic: the sources disagree among themselves on a trailing +blank line and on casing (one uses `[renderers]`, the rest `[Renderers]`), and NotationFile +compares with `_stricmp` throughout. + +Two content oddities preserved rather than "corrected": + +* V4H's Champion destroyed files are named **`champion_stroyed`** - the "de" is missing. That + is the name their build actually uses, so the generated `.data` references + `champion_stroyed_solid.obb` and `champion_stroyed.video` to stay self-consistent. +* Our own `Grimreaper_destroyed/grimreaper_Destroyed.video` points at + `grizzly_destroyed.erf`. Pre-existing bug in our tree, unrelated to this work. + +Note we already hold complete original source for two of the six in our own repo -- +`Content/Mechs/Dasher_DNU` and `Content/Mechs/jenner2c` -- so those can be cross-checked +against the decompiled output rather than taken on trust. (V4H's Jenner IIC is a *different* +asset from ours: their part prefix is `jec_` and chassis files are `jenner_2c.*`, ours are +`j2c_` and `jenner2c.*`, with zero files in common.) + +Still to build for the six main chassis: `.data` (values live in `X.data{GameModel}`), +`.damage` (`MWObject::CreateDamageStream` -> +`MWInternalDamageObject::ConstructMWInternalDamageObjectStream`), `.contents`, `.instance`, +`.torso`, `.engine`. + +--- + +## 12. Hazards + +* **Filename case.** V4H's packer lowercased every entry name; ours did not. The same record + is `core/mechs/longbow/longbow.data{element}` there and + `core/Mechs/Longbow/longbow.data{Element}` here. Compare case-insensitively; `diff -r` + between the trees is meaningless. +* **Stored-byte md5 is not an equality test.** The same source can pack to different bytes. + `maps/peaks.mw4` showed 9 stored-byte differences and exactly 1 after decompression. Always + decode before believing a difference. +* **This environment writes files through a CP949 locale.** Any non-ASCII character written + into a repo file gets mangled -- both a shell heredoc and the editor tool did it. The + section sign became byte pair A1 D7, the right-arrow became A1 E6, and em dashes were lost + to literal question marks; line endings also flipped to CRLF. This is the same class of + hazard `CLAUDE.md` documents for the Korean-comment sources. + **Keep these documents pure ASCII** -- write `->` not an arrow, `x` not a multiplication + sign, `--` not an em dash. After editing, check with: + `python3 -c "open('f','rb').read().decode('ascii')"`. +* **`_merged/` uses hardlinks.** Editing a file there edits the per-package copy too. +* **Duplicate page names are legal** in these NotationFiles and the packer resolves them + inconsistently. Never assume page name is a key. diff --git a/MW4COMPARE/README.md b/MW4COMPARE/README.md new file mode 100644 index 00000000..e5377ae4 --- /dev/null +++ b/MW4COMPARE/README.md @@ -0,0 +1,732 @@ +# MW4COMPARE -- FireStorm source tree vs. the `FS_Build_V4H` binary build + +Comparison of our source repository against **FireStorm Battletech SMT Build Four (v4h)**, +a parallel, independently-maintained *binary-only* build of the same game, located at +`/home/rich/Repositories/FS_Build_V4H` (1.7 GB, dated Oct/Nov 2021). + +Goal: work out what each side has that the other does not, so the two can eventually be +merged into one build containing all the mechs, maps, game types and variants. + +**Status: Part 1 (survey) complete. Part 2 (extraction + pruning) complete.** Nothing has +been merged into the repo. Every V4H asset is unpacked to a parallel tree outside the repo +at `/home/rich/Repositories/FS_Build_V4H_extracted`, reduced to **differences only** -- +5,385 files / 65 MB out of an original 46,707 files / 1.24 GB -- and reshaped to mirror +`Gameleap/mw4`. See section 5. + +--- + +## 0. TL;DR + +| | V4H has, we don't | We have, V4H doesn't | +|---|---|---| +| **Mechs** | **6 new chassis** (IDs 65-70): Champion, Jenner IIC, Dasher, Marauder, Thunderbolt, Griffin -- full geometry, textures, HUD/MFD/radar art, footsteps | Source for ~25 unregistered/experimental chassis (Blackheart, Bowman, Canis, Crab, Gargoyle, koto, locust2c, marauder2, pitbull, privateer, razorback, reaper, reaver, shadowhawk, strider, urbanmech_iic, ursus, vulture2, vulturec, ...) plus 4 Elite\_\* pilot variants | +| **Variants** | **797 saved mechlab variants** (`resource/Variants/*.mw4`); we ship 1 | -- | +| **Maps** | none | **19 maps** (conroe01-03, desert, doneg01, gage, minerl01, mountn01/03, ngoth, ruin03, scrub01, scrub06, volcan01/03, ddc_msl, freezer, ...) | +| **Missions** | none | **17 missions** + 5 UserMissions (aspen, canyon, conroe01-03, gagetown, gladiatorpit, lakeside, mechworks, minehq, newgothem, rubble, sanddunes, spaceport, vbase, editortemplate) | +| **Skies** | *missing entirely* -- no `skies.mw4` | `skies.mw4` (40 MB, 2975 entries) | +| **Shell scripts** | reworked MechLab + 11 loose script/header files we don't have at all | our own console/lobby work (RookieMission, `-tmfds`, etc.) | +| **Engine** | binary only -- cannot be recovered | full source | + +The single most important structural finding: + +> **Mech IDs 0-64 are byte-for-byte identical in both forks.** V4H *appended* its six new +> mechs as IDs 65-70. There is no re-indexing to do -- a merge is additive on both sides. + +--- + +## 1. What `FS_Build_V4H` is + +A deployed game folder, not a source tree. Notable contents: + +``` +FS_Build_V4H/ + MW4.exe 3,633,152 B 2021-09-08 (their fork; no source available) + ScriptStrings.dll 163,840 B 2021-02-03 (has the 6 new mech display names) + MissionLang.dll 188,416 B 2021-09-01 + content/ 14 loose files (shell scripts + 1 ABL header) + hsh/ 279 files -- the "block HUD" set introduced in v4f + hshoriginal/ 447 files -- the pre-v4f HUD set, archived + resource/ + core.mw4 12,709 entries + props.mw4 10,779 entries + textures.mw4 10,419 entries + maps/ 27 maps Missions/ 29 missions + Variants/ 797 saved mechlab loadouts + Pilots/Tesla/options.mw4 + (no skies.mw4) + stats/zWorldStats/ stock 2001-era MS stats uploader, logs end 2001 -- dead weight + uploadbuildv4h/ 777 MB self-extracting installer of this same build + firestorm build V4h.txt changelog for v4 -> v4h (also in uploadbuildv4h/) +``` + +Their own changelog (`firestorm build V4h.txt`) is worth reading; it documents the +six new mechs, the 3-ton-per-weapon ammo cap, the MechLab save crash, and the fact that +corrupt variants can corrupt the whole build. + +--- + +## 2. Method + +The `.mw4` files are GameOS `Stuff::Database` packages (magic `#VBD`) -- a flat directory of +named records, each optionally LZW-compressed. See `tools/mw4db.py` for the format and a +faithful port of `gos_LZDecompress`. + +Two comparison levels were used: + +1. **Index-only** (`mw4index.py` + `diffindex.py`) -- parses the directory, no decompression. + ~1 s for the whole 780 MB tree. Gives an exact **entry-name** diff. +2. **Decoded** (`pkgcmp.py`) -- decompresses every record and compares plaintext bytes. + Slower (~2 MB/s) but is the only trustworthy content test. + +### Two traps that cost time -- do not repeat them + +* **Case.** V4H's packer lowercased every entry name (`ablscripts\bots\0-bot.abl` vs our + `ablscripts\bots\0-Bot.abl`). A case-sensitive comparison reported 5,909 "new" and 5,155 + "missing" entries in `core.mw4` that were the *same files*. Always compare through + `mw4db.norm()`. +* **Stored-byte md5 is not an equality test.** Comparing the compressed payloads + over-reports massively: `maps/peaks.mw4` showed 9 differing blobs by stored bytes but + only **1** after decompression. Use `pkgcmp.py` before believing any difference. + +--- + +## 3. Findings + +### 3.1 The six new mechs (the headline) + +`core.mw4` in V4H is a **strict superset** of ours: 754 extra entries, **zero** entries we +have that they lack. All 754 belong to six new chassis and their destroyed models: + +| chassis | core entries | textures | props (animation) | +|---|---|---|---| +| marauder | 63 armaturedata + 49 root + 21 armaturevideo + 5 destroyed | 37 + 1 | reuses existing | +| griffin | 57 + 54 + 19 + 5 | 38 + 1 | reuses existing | +| champion | 57 + 51 + 19 + 5 | 36 + 1 | reuses existing | +| thunderbolt | 54 + 48 + 18 + 5 | 33 + 1 | reuses existing | +| dasher | 45 + 48 + 15 + 5 | 29 + 1 | reuses existing | +| jenner2c | 45 + 46 + 15 + 5 | 30 + 1 | **155 new entries** under `mechs/jenner2c/animation` | + +Each chassis brings the full set: `.contents` (+ per-joint `{armature}` / `{sites}` pages), +`.damage`, `.data` (+ `{element}` `{footsteps}` `{gamemodel}` `{hierarchicalobb}` `{solidobb}`), +`.engine`, `.instance`, `.subsystems`, `.torso`. + +`textures.mw4` adds 299 entries: the six mechs' skins, six sets of +`textures/@a{chp,grf,jec,mar,thu,...}0-5.tga` cockpit/pilot art, and +`textures/footsteps/_{default,dirt,snow}.tga`. + +**Nothing else is new in `core.mw4`.** No new weapons, no new subsystem types, no new +tables. Whatever balance changes V4H made live in the per-mech `.subsystems` records and +in their `MW4.exe`. + +### 3.2 Mech ID space -- identical for 0-64 + +`content/ABLscripts/mwconst.abi` differs from ours by exactly this: + +```diff +-LastMechID = 64 ; +-NoMechID = 65 ; ++M_Champion = 65 ; ++M_Jenner2c = 66 ; ++M_Dasher = 67 ; ++M_Marauder = 68 ; ++M_Thunderbolt = 69 ; ++M_Griffin = 70 ; ++LastMechID = 70 ; ++NoMechID = 71 ; ++LastMP1MechID = M_Solitaire; +``` + +Corroborated three ways: + +* `content/shellscripts/mechbay/mechnames.script` and `buildmechnames.script` list + `mech_name[0..64]` exactly matching our `mechnames[]` in + `CoreTech/Libraries/GameOS/render.cpp`, then append 65-70. +* `core.mw4` defines 65 `*.subsystems` on our side, 71 on theirs; the extra six are + `champion, dasher, griffin, jenner_2c, marauder, thunderbolt`. +* `ScriptStrings.dll` (UTF-16) gains display strings `Champion`, `Jenner2c`, `Dasher`, + `Marauder`, `Thunderbolt`, `Griffin`. + +Note the naming inconsistency in their own build: the chassis directory is `jenner2c` +but the subsystems record is `jenner_2c`, while three of their variants still reference +`jenner_iic1` (see section 3.3). + +### 3.3 797 mech variants + +`resource/Variants/*.mw4` -- saved MechLab loadouts. We ship exactly one +(`MW4/Resource/Variants/Argus Variant1.mw4`). + +Each variant package is 4 records: + +``` +[0] 'Resource\Variants\' 4 B raw +[1] '{Mech}' ~350 B +[2] '{Subsystem}' ~4 KB <- names the chassis it needs +[3] '<8-char token>' 16 B +``` + +Because the chassis is named in record 2, dependency analysis needs no decompression +(`tools/variants-report.py`). Result: + +``` +loadable in our build TODAY : 727 +blocked on the 6 new chassis: 67 (thunderbolt 22, griffin 15, marauder 15, + dasher 10, champion 5) +broken / orphaned : 3 (chassis 'jenner_iic1' does not exist even in V4H -- + Jenner2c 1 / Jenner2c 4 / Jenner2c Pharaoh) +``` + +Top chassis by variant count: warhammer 36, rifleman 30, nova 24, madcat 23, atlas 23, +thunderbolt 22, hunchback 21, battlemaster 21, archer 21, thor 20. + +Full table: `reports/variants-inventory.txt`. + +### 3.4 Maps and missions -- we are ahead, they changed a little + +* **Maps**: V4H has 27, we have 46. **Zero** V4H-only maps. Common maps are effectively + identical -- file sizes differ by 0-2 bytes and `peaks.mw4` decodes to exactly **one** + differing record (`peaks.data{gamemodel}`, an internal reference blob). +* **Missions**: V4H has 29, we have 46 + 5 UserMissions. **Zero** V4H-only missions. + Every common mission decodes to 2-4 differing records, always the same shape: + * `.lights` and `night.lights` -- **3 bytes** differ out of 33 KB. Noise. + * `.contents` -- **real**. Coliseum: 11,588 of 52,954 bytes differ, and the + differing region contains new ASCII entity names. Their mission layouts have been + edited. Worth a proper look before merging either direction. + * `Missions/freezer.mw4` additionally has **30 V4H-only records**: stadium crowd VO + (`audio/vo/generic/mp/{cheer1-9,boo1-3,jeers,roar,ahh,aww,stadamb}.wav`) and a + rewritten `scripts/freezer_teamattrition.abl` (10,710 B vs our 832 B). +* All 71 loose `.nfo` / `.tga` / `.txt` files in `resource/` (mission metadata, game-type + definitions, thumbnails) are **byte-identical**. No game-type divergence. +* **`skies.mw4` is absent from V4H entirely.** 40 MB / 2,975 entries that only we have. + +### 3.5 Shell scripts -- V4H has a diverged MechLab and 11 files we don't have at all + +`props.mw4` decodes to 419 differing records, only 3 entries missing on their side +(`lobbydecals/decal_46/47/49.tga`) and 155 extra (jenner2c animation). +The interesting differences (V4H size vs ours): + +| script | V4H | ours | delta | +|---|---|---|---| +| `mechbay/mechbay_main.script` | 94,517 | 61,432 | **+33 KB** | +| `mechbay/weapons.script` | 102,025 | 83,653 | **+18 KB** | +| `mechbay/armor.script` | 64,539 | 61,807 | +2.7 KB | +| `mechbay/infobox.script` | 43,875 | 41,595 | +2.3 KB | +| `listboxes.script` | 30,785 | 29,269 | +1.5 KB | +| `scriptstrings.h` | 71,608 | 70,811 | +797 B | +| `netlobby.script` | 163,740 | 163,088 | +652 B | +| `multiplayer/buildrestriction.script` | 16,443 | 15,713 | +730 B | +| `mechbay/chassis.script` | 82,596 | 81,881 | +715 B | +| `conlobby.script` | 141,044 | 140,532 | +512 B | +| ...plus ~15 more with small deltas | | | | + +Also: 24 `mechbay/graphics/installed/weapon_-1_*.tga` slot images are **much smaller** in +V4H (1,036 B vs 6,540 B etc.) -- this is the "weapon box sizes set to minimum" change from +their v4 changelog. + +**Eleven loose files in `content/shellscripts/` exist in V4H and nowhere in our repo:** + +``` +clock.script wall-clock overlay +cm_listboxes.script console mech list box +firestorm.h 20 console callsigns +firestorm64.h 64 Dragon-MP callsigns +globals.script shared script globals +options.h USE_ALLOWED_MECHS, Battle_Ammo, USE_OBSERVER, ... +mechbay/buildmechnames.script 71-entry mech name table +mechbay/mechnames.script 71-entry mech name table +mechbay/mechdescriptions.script 74 KB of mech description text +mechbay/old mechdescriptions.script +graphics/multiplayer/lobbydecals/decal_.tga +``` + +These are loaded loose from disk (they are in neither `core.mw4` nor `props.mw4` on either +side). Their `computerplayer.script` `#include`s `firestorm.h` where ours `#include`s +`callsign.h` -- so this is a genuinely diverged shell-script codebase, not just extra data. + +### 3.6 HUD art (`hsh/`) + +V4H maintains **two** HUD sets: + +* `hshoriginal/` (447 files) -- the detailed paper-doll set, close to ours. +* `hsh/` (279 files) -- the "block HUD" compromise introduced in v4f so the new mechs + would have *something* to display. Their changelog admits HUDs do not always match the + mech actually in use. + +Against our `hsh/` (456 files): + +| | identical | differ | V4H-only | ours-only | +|---|---|---|---|---| +| `hsh/` | 182 | 68 | 29 | 206 | +| `hshoriginal/` | 275 | 148 | 24 | 33 | + +V4H-only files are the six new mechs' `hud/`, `MFD/`, `radar/hud/` and `Mechs/` art, +`decals/DDC.bmp`, plus `Mechs/battlemasteriic.bmp` and `Mechs/mad cat mk.ii.bmp` +(pre-rename spellings of files we renamed). + +Colour depth differs systematically: their `Mechs/*.bmp` are 24-bpp 500x400 (600,054 B), +ours are 8-bpp palettised (201,080 B). Same for `MFD/rifleman.bmp` (43,256 vs 15,476). + +`hsh/hud/Convert.txt` and `hsh/radar/hud/Convert.txt` are **the authoring recipe for the +paper-doll art** and explicitly say the coordinates go into `coord.cpp` under +GameOS/External dependencies. Independent corroboration of `MFD-RADAR-MAPPINGS.md`. +They are reproduced verbatim in `reference/Convert-hud.txt` / `reference/Convert-radar.txt`. + +### 3.7 Per-mech `.subsystems` differ for every single chassis + +Every one of the 65 shared chassis has a differing `.subsystems` record -- same length, +different content (Atlas: 1,830 of 6,082 bytes). This is the compiled form, not the source +text, so it is not directly readable. It is consistent with V4H's documented changes: +3-ton ammo cap per weapon, revised default weapon groups, restricted hardpoints on the new +mechs. + +**This is the one area where merging is genuinely risky** -- see the open questions. + +Also differing in `core.mw4` for essentially every mech: `.data{gamemodel}`, +`.torso{gamemodel}`, and the `contents[joint_torsoabove]{armature}` / +`contents[joint_*belowankle]{armature}` records. Byte inspection of +`atlas.data{gamemodel}` shows one side stores an inline path string where the other +stores a numeric reference -- i.e. a packer/format difference, most likely benign. +Confirm before treating any of these as content changes. + +### 3.8 Root config and binaries + +Identical: 37 root files, including all three `ctcl-*.ini`. + +Differing binaries (theirs older/forked, ours newer): `MW4.exe`, `autoconfig.exe`, +`Launcher.exe`, `mw4print.exe`, `ctcls.dll`, `Language.dll`, `MissionLang.dll`, +`ScriptStrings.dll`. + +V4H-only root files: `Console launcher.bat` (`mw4.exe -window`), `optionssmt.ini`, +`optionssmtv4.ini`, `options.ini.rp411old`, `pre-uninstall.bat`, `RMTSHARE.EXE`, +`SetNetworkAccess.reg`, `firestorm build V4h.txt`. + +`options.ini` differences worth flagging: + +| key | V4H | ours | +|---|---|---| +| `bitdepth` | **32** | 16 | +| `ruletype` | 17 | 2 | +| `unlimitedammo` | 0 | 1 | +| `friendlyfirepercentage` | 100 | 0 | +| `heaton` | 1 | 0 | +| `allowedbeam1` | `fffffffd` | `ffffffff` | +| `allowedmissile1` | `ff3bffff` | `ffffffff` | +| `allowedprojectile1` | `ffffdfff` | `ffffffff` | +| `playericon` / `teamicon` | decal\_49 / decal\_50 | decal\_48 / decal\_48 | +| `[RookieMission]` section | absent | present (ours) | +| `RuleBook`/`DawnWar`/`BiggieSizeIt`/`CanYouHearTheFootSteps` | absent | present (ours) | + +`bitdepth=32` is interesting given all the `DWM8And16BitMitigation` work recorded in the +repo memory -- V4H apparently runs the console at 32 bpp. + +`optionssmtv4.ini` additionally carries a full `[cameraship params]` block (over-shoulder +offsets, death-sequence camera, HUD name/chat toggles) that ours does not. + +### 3.9 What *we* have that V4H does not + +Besides the maps/missions/skies above, `Gameleap/mw4/Content/Mechs` contains 93 +non-destroyed chassis directories, of which **27 are not in V4H's 71-mech roster**: + +``` +Blackheart Bowman Canis Crab Duangung Gargoyle Gesu Grimreaper_DNU +koto locust2c marauder2 pitbull privateer razorback reaper reaver +shadowhawk strider urbanmech_iic ursus vulture2 vulturec +Elite_Daishi_Castro Elite_Daishi_William Elite_Madcat_James Elite_Thor_Duncan +jenner2c (chassis 'jenner2c' -- V4H spells theirs 'jenner_2c') +``` + +Two of these matter immediately: + +* **`Dasher_DNU/`** -- chassis `dasher`, 71 source files including `.subsystems`, + `.damage`, `.animscript`, geometry. We already have Dasher source; it is simply not + registered in the roster or packed into `core.mw4`. +* **`jenner2c/`** -- 80 source files, plus `jenner2c_destroyed/`. Likewise unregistered. + +So of V4H's six new mechs we may already hold source for two. `marauder2` is *Marauder II*, +a different chassis from their `marauder`. + +--- + +## 4. Tools + +All under `tools/`. Python 3, no dependencies. + +| file | what it does | +|---|---| +| `mw4db.py` | `#VBD` package reader + `gos_LZDecompress` port. Import this; everything else builds on it. Documents the on-disk format. | +| `mw4index.py` | `mw4index.py > manifest.tsv` -- directory-only manifest of every `*.mw4`. ~1 s for 780 MB. | +| `diffindex.py` | `diffindex.py A.tsv B.tsv [labelA] [labelB]` -- case-insensitive package/entry-name diff. | +| `pkgcmp.py` | `pkgcmp.py A.mw4 B.mw4` -- **authoritative** decoded content diff of one package. | +| `dumprec.py` | `dumprec.py pkg.mw4 "entry/name" [out]` -- extract one decoded record; `--list` lists entries. | +| `treediff.py` | `treediff.py dirA dirB [labelA] [labelB]` -- case-insensitive loose-file tree diff by md5. | +| `variants-report.py` | chassis-dependency inventory of `resource/Variants`, flags variants that cannot load. | +| `extract-all.py` | `extract-all.py ` -- unpack every package into a real directory tree. See section 5. | +| `prune-identical.py` | `prune-identical.py [--apply]` -- delete everything we already have byte-for-byte. Dry-run by default. | +| `classify-survivors.py` | `classify-survivors.py ` -- split what is left into NEW vs DIFFERS. | +| `restructure.py` | `restructure.py [--apply]` -- reshape a pruned tree to the repo's layout and casing. Refuses to run if any two packages would collide. | +| `split-source.py` | `split-source.py [--apply]` -- separate repackable source from compiled records and rename the `.obb`s. | + +`run-comparison.sh` regenerates every report: + +```bash +cd MW4COMPARE +./run-comparison.sh # uses the default paths +./run-comparison.sh /path/to/OTHER_BUILD /path/to/our/mw4 +``` + +Runtime is a few minutes; `textures.mw4` dominates. + +There is an older, standalone version of the same decompressor at +`build-env/extract-mw4.py`. `tools/mw4db.py` supersedes it and is the one to extend. + +## 5. The extracted asset tree + +Every record of every V4H package, unpacked to real files at +**`/home/rich/Repositories/FS_Build_V4H_extracted`** -- deliberately *outside* the repo. +Nothing has been merged; this is derived data and is regenerable: + +```bash +python3 MW4COMPARE/tools/extract-all.py \ + /home/rich/Repositories/FS_Build_V4H/resource \ + /home/rich/Repositories/FS_Build_V4H_extracted +``` + +857 packages -> **46,707 files, 1.24 GB**, about 3 minutes. + +| directory | files | size | from | +|---|---:|---:|---| +| `core/` | 12,709 | 3.4 MB | `core.mw4` | +| `props/` | 10,779 | 178 MB | `props.mw4` | +| `textures/` | 10,419 | 358 MB | `textures.mw4` | +| `maps//` | 7,219 | 576 MB | 27 map packages | +| `Missions//` | 2,391 | 60 MB | 29 mission packages | +| `Variants//` | 3,188 | 4.8 MB | 797 saved loadouts | +| `Pilots/Tesla/options/` | 2 | 5 KB | | +| `_merged/` | 42,100 | *hardlinks* | flattened Content-shaped view | + +Extraction is **per package**, because 842 entry paths are claimed by more than one +package and 70 of those hold genuinely different content (mostly a mission packing its own +copy of a global `props.mw4` asset -- 29 are Coliseum/Freezer crowd VO). A single flat tree +would silently lose data. + +`_merged/` is a convenience view built with **hardlinks** (no extra disk) that mirrors +`Gameleap/mw4/Content`, so it diffs directly against our source tree. Conflicts resolve +`props > core > textures > maps > missions` and every one is listed in `_conflicts.tsv`. +Variants and Pilots are excluded from it -- their records have no directory part +(`{Mech}`, `{Subsystem}`) so all 797 variants would collide. +Treat `_merged/` as read-only; hardlinks mean editing there edits the per-package copy. + +Full details, including the duplicate-entry handling, are in the tree's own `_README.txt`. + +### How close packed records are to real source + +Verified against `mechs/jenner2c`: + +* **Recovered as-is** -- `.contents`, `.damage`, `.data`, `.engine`, `.instance`, + `.subsystems`, `.torso`, `.animscript`, every `*.erf` geometry part, `*_cage.erf`, + `runninglights.erf`, `armaturedata/`, `armaturevideo/`. Decoded TGAs are valid + (`Targa image data - RGB 512 x 512 x 32`); `core/ablscripts/mwconst.abi` extracts + byte-identical to V4H's loose copy, which independently validates the decompressor. +* **Recoverable by renaming a qualified record** -- + `X.data{solidobb}` -> `X_skeleton_SOLID.obb` and + `X.data{hierarchicalobb}` -> `X_skeleton.obb`. Both carry the `#BBO` magic and + byte-identical headers to our source `.obb` files. +* **Not directly present** -- `X.armature`. The packer splits it into per-joint + `X.contents[joint_*]{armature}` records. + +Also worth knowing before any merge: **V4H's Jenner IIC is not a rename of ours.** Their +part prefix is `jec_` and chassis files are `jenner_2c.*`; ours are `j2c_` and +`jenner2c.*`. Zero files in common. They are separately authored assets. + +### Pruning to differences only + +Our own packages were extracted the same way to +**`/home/rich/Repositories/FS_Ours_extracted`** (102 packages, 51,382 files, 1.76 GB), +then everything V4H has that we already hold byte-for-byte was deleted: + +```bash +python3 MW4COMPARE/tools/prune-identical.py /home/rich/Repositories/FS_Build_V4H_extracted --apply +python3 MW4COMPARE/tools/classify-survivors.py /home/rich/Repositories/FS_Build_V4H_extracted +``` + +**46,707 files / 1.24 GB -> 5,385 files / 66 MB.** 41,322 deleted (88.5%), 1,912 empty +directories removed. `_pruned.tsv` logs every deletion and which rule matched. + +| package group | NEW | DIFFERS | | +|---|---:|---:|---| +| `Variants` | 3,188 | 0 | 797 loadouts x 4 records; we ship 1 | +| `core` | 746 | 440 | 6 new mechs + per-chassis changes | +| `props` | 155 | 419 | jenner2c animation + reworked MechLab | +| `textures` | 297 | 2 | new mechs' skins / footsteps | +| `Missions` | 1 | 114 | `.contents`, `.lights`, freezer crowd VO | +| `maps` | 0 | 21 | | +| `Pilots` | 0 | 2 | | +| **total** | **4,387** | **998** | | + +#### Why our source tree alone is not a sufficient baseline + +A `.mw4` is not a zip of `Content/`. Measured: + +* **17,683 records (42%)** are qualified derivatives the packer *generates* and that have + no source file at all -- `foo.data{gamemodel}`, `foo.contents[joint_hip]{armature}`, + `bar.tga{hint}`. +* **~7,400 more** have a source path but the packer **rewrites the bytes**. Proven by + comparing *our own* `Content/` against *our own* `.mw4` output: 3,238 `.data`, + 2,320 `.video`, 583 `.instance`, 361 `.contents`, 345 `.audio`, 235 `.damage`, + 117 `.torso`, 103 `.subsystems`, 65 `.engine`, 58 `.lights`. + +Matching against `Content/` alone would have "kept" roughly 24,000 files we already have. +So each candidate is tested in order: (1) same package + entry path in our extracted +packages, (2) same entry path in any of our packages, (3) unqualified name at the same path +in `Content/`. In practice 41,320 matched rule 1 and 2 matched rule 3. + +> **Case warning.** V4H's packer lowercased every entry name; ours did not. The same record +> is `core/mechs/longbow/longbow.data{element}` in V4H and +> `core/Mechs/Longbow/longbow.data{Element}` in ours. Every comparison here is +> case-insensitive -- a plain `diff -r` between the two trees is meaningless. + +#### Reading the 998 DIFFERS + +By extension: 352 `data{gamemodel}`, 96 `torso{gamemodel}`, 93 `subsystems`, +114 `contents[joint_*]{armature}`, 55 `lights`, 54 `audio`, 38 `instance`, 37 `contents`, +29 `script`, 28 `tga`, 15 `wav`. + +* **Probably packer noise** -- the `{gamemodel}` records encode cross-package references + (one side stores an inline path string where the other stores a numeric id), and the + `.lights` differences measured 3 bytes out of 33 KB. Verify before treating as content. +* **Real, needs a design decision** -- the 93 `.subsystems` (V4H's balance pass) and the + 37 mission `.contents` (substantial layout edits). +* **Real, straightforward** -- the 29 `.script` files (their MechLab rework) and the + freezer crowd VO. + +### Reshaped to the repo layout + +Once pruned, the V4H tree has **zero cross-package collisions** -- 5,385 files map to 5,385 +distinct destinations -- so it was flattened to the repo's shape: + +```bash +python3 MW4COMPARE/tools/restructure.py /home/rich/Repositories/FS_Build_V4H_extracted --apply +``` + +``` +FS_Build_V4H_extracted/ + Content/ same shape and casing as Gameleap/mw4/Content + Resource/Variants// 797 saved loadouts (records have no path) + Resource/Pilots/Tesla/options/ +``` + +Path components are re-cased against the repo wherever a counterpart exists, so +`mechs/longbow/longbow.data{element}` became +`Content/Mechs/Longbow/longbow.data{Element}`. Components with no counterpart keep the +packer's spelling -- which is why the six new chassis sit at `Content/Mechs/champion`, +`dasher`, `griffin`, `jenner2c`, `marauder`, `thunderbolt` in lower case. +`_layout.tsv` records the originating package and entry name for every file, so the +per-package view can be reconstructed at any time. + +**`FS_Ours_extracted` is deliberately left in per-package form.** Unpruned it still has 60 +genuine collisions (mission-local copies of shared `Culturals/` props, e.g. +`arena_debris_large01.data` differing across Coliseum / Reduex / StormCanyon) plus 2,617 +harmless duplicate paths. Flattening it would lose data, and it is regenerable in ~4 minutes +anyway. `restructure.py` refuses to run on a tree with collisions for exactly this reason. + +### Source vs compiled -- only 502 of the 2,195 files are repackable + +**A `.mw4` does not store the source tree.** Some records are the source file byte for byte; +others are what the packer produced from it. Which is which was established empirically by +hash-matching every record of *our own* packages against `Gameleap/mw4/Content` +(22,138 matched a source file, 28,728 did not): + +| | | +|---|---| +| **repackable source** | `.tga` `.erf` `.mw4anim` `.bid` `.wav` `.bsp` `.material` `.abl` `.obb` `.ebf` `.bounds` `.animscript` `.fgd` `.tcf` `.mlr` `.d3f` `.gaf` `.script` `.h` `.abi` | +| **compiled** | `.data` `.instance` `.subsystems` `.damage` `.torso` `.engine` `.audio` `.video` `.lights` `.mw4`, and every `{hint}` `{handle}` `{gamemodel}` `{element}` `{footsteps}` `{zones}` `{nametable}` `[shadow]` `[joint_*]{armature}` `[joint_*]{sites}` record | + +The decisive evidence: our source `Content/Mechs/Atlas/atlas.data` is **4,891 bytes of text**; +the record packed under that name is a **12-byte binary stub**. The real payload went into +the `{gamemodel}` / `{element}` records. So a mech's `.data`, `.damage`, `.subsystems`, +`.instance`, `.contents`, `.torso` and `.engine` **cannot be recovered from any package** -- +they must be re-authored. `.armature` does not exist in a package either; the packer splits it +into per-joint `X.contents[joint_*]{armature}` records. + +```bash +python3 MW4COMPARE/tools/split-source.py /home/rich/Repositories/FS_Build_V4H_extracted --apply +``` + +``` +FS_Build_V4H_extracted/ + Content/ 502 files repackable source, repo shape + casing - drop straight in + _compiled/ 1693 files packer output, reference only + Resource/ variants + pilot options (use the original .mw4 files) +``` + +Renames applied -- these genuinely are `.obb` files (`#BBO` magic, headers byte-identical to +ours): + +| record | becomes | +|---|---| +| `champion.data{solidobb}` | `champion_skeleton_SOLID.obb` | +| `champion.data{hierarchicalobb}` | `champion_skeleton.obb` | + +(`_SOLID.obb` / `.obb` outside `Content/Mechs`.) The real source spelling is declared inside +the `.data` text we don't have, so a convention was applied and logged in +`_source-vs-compiled.tsv` -- whatever `.data` gets authored later must reference these names. + +What survives in `Content/`: 208 `.erf`, 154 `.mw4anim`, 73 `.tga`, 29 `.script`, 17 `.obb`, +15 `.wav`, 3 `.h`, 1 each `.animscript` `.abl` `.abi`. + +The resulting `Content/Mechs/dasher/` now matches our own `Content/Mechs/Dasher_DNU/` naming +exactly (`das_hip.erf`, `dasher_cage.erf`, `dasher_skeleton.obb`, `dasher_skeleton_SOLID.obb`). + +> For the six new chassis we therefore have **all geometry, animation and art**, but their +> definition files must be written by hand. Note we already hold complete source for two of +> them in our own repo: `Content/Mechs/Dasher_DNU` and `Content/Mechs/jenner2c`. + +--- + +## 6. Reports + +Written to `reports/` by `run-comparison.sh`. The two `manifest-*.tsv` files are ~5 MB +intermediates and are gitignored. + +| file | contents | +|---|---| +| `packages-entrydiff.txt` | per-package entry-name diff, all 857 V4H packages vs our 102 | +| `core-decoded-diff.txt` | 433 real differences + 754 V4H-only entries in `core.mw4` | +| `props-decoded-diff.txt` | 419 real differences + 155 V4H-only in `props.mw4` | +| `textures-decoded-diff.txt` | 1 real difference + 299 V4H-only in `textures.mw4` | +| `maps-decoded-diff.txt` | per-map decoded diff (all <=1 real difference) | +| `missions-decoded-diff.txt` | per-mission decoded diff (`.contents` / `.lights` / freezer) | +| `hsh-diff.txt`, `hshoriginal-diff.txt` | HUD art trees | +| `content-diff.txt` | the 11 loose scripts + `mwconst.abi` | +| `resource-files-diff.txt` | package-file and loose-file level diff of `resource/` | +| `variants-inventory.txt` | 797 variants grouped by chassis with load status | + +`reference/` holds verbatim copies of small, high-value V4H text files that are not in our +tree, so they survive independently of the `FS_Build_V4H` folder: + +| file | source | +|---|---| +| `v4h-changelog.txt` | `firestorm build V4h.txt` -- their v4 -> v4h release notes | +| `Convert-hud.txt`, `Convert-radar.txt` | `hsh/hud/Convert.txt`, `hsh/radar/hud/Convert.txt` -- paper-doll authoring recipe, points at `coord.cpp` | +| `mechnames.script`, `buildmechnames.script` | the 71-entry mech name tables | +| `v4h-firestorm.h`, `v4h-firestorm64.h` | 20 console / 64 Dragon-MP callsigns | +| `v4h-options.h` | `USE_ALLOWED_MECHS`, `Battle_Ammo`, `USE_OBSERVER`, ... | +| `mwconst.abi.diff` | the `M_Champion..M_Griffin` / `LastMechID=70` delta | +| `options.ini.diff` | ours -> theirs | + +Not copied (too large, read them from `FS_Build_V4H/content/shellscripts/mechbay/`): +`mechdescriptions.script` (74,883 B) and `old mechdescriptions.script` (74,438 B). + +--- + +## 6b. Decompiling the compiled records + +**Full write-up: [DECOMPILING.md](DECOMPILING.md).** Read that before touching any of this. + +The compiled records are not a dead end. The engine source contains both the writer +(`MWTool::BuildResource` -> `MWObject::CreateSubsystemStream` -> `Tool::ConstructCreateMessage` +-> a per-class factory in one of 51 `mw4/Code/MW4/*_Tool.cpp`) and the reader, and the message +structs are plain PODs declared in our own headers. We also have a closed-loop test harness: +65 chassis where we hold both the source text and the compiled record. + +| record type | status | +|---|---| +| `.armature` | **done and verified** -- 2,938 of 2,976 pages exact across 65 chassis, 1,849/1,849 child lists exact. All 38 residuals are data the packer itself discarded, so the game never sees them either. | +| `.subsystems` | **done and verified** -- 7,579 of 7,585 keys exact across 63 chassis. The 6 residuals are Behemoth/Behemoth II `GroupIndex`, changed in source by `e45a67a8` after the package was built. | +| `.data` `.damage` `.contents` `.instance` `.torso` `.engine` | same mechanism, not started | + +```bash +python3 MW4COMPARE/tools/decompile/armature.py --verify # 65-chassis harness +python3 MW4COMPARE/tools/decompile/armature.py -o out.armature +``` + +`.armature` files have been generated for all six new chassis into +`FS_Build_V4H_extracted/Content/Mechs/*/`. + +Two things found along the way that matter beyond this section: + +* The extracted baseline reflects the **packages**, not `Content/` at HEAD. `core.mw4` and + `textures.mw4` have never been repacked since the initial mirror (`2b8ca921`); `props.mw4` is + current as of `8bfaf9b9`. Check this before concluding anything is missing. See + DECOMPILING.md section 8. +* `ResourceID` packs the package record id in its **high word**, which is how `Model=` + references resolve back to a path. + +--- +## 7. Merge assessment + +Ranked by value/risk. **None of this has been done.** + +### Low risk, high value -- take from V4H + +1. **727 mech variants** that reference chassis we already have. Drop-in: they are + self-contained `.mw4` files in `Resource/Variants/`, keyed by chassis *name*, and our + chassis names match. Sanity-check a sample in the MechLab first -- their changelog warns + that a corrupt variant can corrupt a build. +2. **The 11 loose shell scripts** -- at minimum `mechdescriptions.script` (74 KB of mech + text we simply do not have) and the two mech-name tables. `firestorm.h`/`firestorm64.h` + callsign lists are a straight content lift. +3. **Freezer stadium crowd VO** (17 wavs) and the expanded `freezer_teamattrition.abl`. +4. **HUD/MFD/radar art for the six new mechs**, and the 24-bpp `Mechs/*.bmp` masters if we + want higher-quality portraits than our 8-bpp copies. + +### Medium risk -- the six new mechs + +The assets are all extractable from `core.mw4`/`textures.mw4`/`props.mw4`. The blockers are +on *our* side and are all code, not data: + +* `render.cpp` `mechnames[]` -- append 6 entries (currently 65, `"dasher"` is commented out). +* `coord.cpp` -- MFD/radar paper-doll coordinate tables must gain 6 entries. +* `mwconst.abi` -- apply the `M_Champion..M_Griffin` / `LastMechID=70` delta. +* `ScriptStrings.dll` (`StringResource.rc`) -- add `DNL_CHAMPION`, `DNL_JENNER2C`, + `DNL_DASHER`, `DNL_MARAUDER`, `DNL_THUNDERBOLT`, `DNL_GRIFFIN`. +* MechLab scripts must learn about IDs 65-70 (this is what their +33 KB + `mechbay_main.script` is doing). +* `MW4Shell.cpp` / anything else with a hard-coded 65. + +Because IDs 0-64 are identical, this is purely additive. + +Extracting the mech *source* (rather than the packed records) is the open question -- the +records in `core.mw4` are the compiled forms, and we need `.erf`/`.obb`/`.data` sources to +rebuild `core.mw4` with our own packer. We already hold source for `dasher` and `jenner2c`. + +### High risk -- do not merge blind + +* **`.subsystems` for the 65 shared chassis.** All 65 differ. Taking theirs imports their + entire balance pass (3-ton ammo cap, revised weapon groups) wholesale; taking ours keeps + our balance. This is a *design* decision, not a merge decision. +* **Mission `.contents`.** Real, substantial edits on their side for every shared mission. + Needs a proper structural diff before any decision. +* **MechLab scripts.** Their MechLab is documented (by them) as crash-prone on save. + Do not import their `mechbay_*.script` wholesale. + +### Not worth taking + +`stats/zWorldStats` (dead 2001 telemetry), `RMTSHARE.EXE`, `uploadbuildv4h/` (777 MB +installer of the same content), `options.ini.rp411old`. + +--- + +## 8. Open questions for the project owner + +1. **Balance.** Do we adopt V4H's `.subsystems` (3-ton ammo cap, their weapon groups) for + the 65 shared mechs, keep ours, or merge selectively per mech-- +2. **Mission layouts.** Their `.contents` differ for every shared mission. Is that + deliberate content work on their side (more drop zones? different props?) or drift we + should ignore-- +3. **Mech source.** Is there a source tree for V4H anywhere, or is `core.mw4` the only copy + of the six new mechs? That decides whether we re-pack from source or have to write an + unpacker that reconstructs `.erf`/`.obb` from packed records. +4. **`bitdepth=32`.** V4H runs the console at 32 bpp; we run 16. Given the + `DWM8And16BitMitigation` dependency documented in the repo memory, is their build + actually rendering at 32 bpp, and should we try it-- +5. **The 25 unregistered chassis on our side** (Canis, Gargoyle, razorback, reaver, ...) -- + are these finished assets that were never wired up, or abandoned work? If usable, the + combined roster could be far larger than 71. +6. **Variant hygiene.** Their changelog warns that corrupt variants can corrupt the build. + Do we import all 727 loadable variants, or curate-- +7. **`jenner_iic1`.** Three variants reference a chassis that exists in neither build. + Drop them, or re-point at `jenner_2c`-- + +--- + +*Generated 2026-08-08 against `FS_Build_V4H` (Oct/Nov 2021) and branch `5.1.0b-in-progress`.* diff --git a/MW4COMPARE/reference/Convert-hud.txt b/MW4COMPARE/reference/Convert-hud.txt new file mode 100644 index 00000000..4552262c --- /dev/null +++ b/MW4COMPARE/reference/Convert-hud.txt @@ -0,0 +1,15 @@ +take 1024 image +reduce image to 320 +expand canvas to 340 (centered) +Using a 4x4 black line seperate the sections +***save as unexploded*** +split components (no overlaps from upper left to lower right of component) +record coords from upper left to lower right, use even numbers. +(x,y,x1,y1) +Now using the Unexploded view (image in upper left corner) overlay the exploded pieces and record the upper left corner coord. +(x2,y2) +expand canvas to 512x512 (upper left corner) +Save the exploded view in Index Mode. + +Modify file(s): +coord.cpp (under GameOS/External dependancies) diff --git a/MW4COMPARE/reference/Convert-radar.txt b/MW4COMPARE/reference/Convert-radar.txt new file mode 100644 index 00000000..3cb0c7a2 --- /dev/null +++ b/MW4COMPARE/reference/Convert-radar.txt @@ -0,0 +1,15 @@ +take 1024 image +reduce to 400 +expand canvas to 410 (centered) +expand canvas to 512 (upper left corner) +***Save this as Unexploded View*** +split components (no overlaps from upper left to lower right of component) +Outline components in white 2x2 pixel +record coords from upper left to lower right, use even numbers. +(x,y,x1,y1) +Now using the Unexploded view (image in upper left corner) overlay the exploded pieces and record the upper left corner coord. +(x2,y2) +Save the exploded view in Index Mode. + +Modify file(s): +coord.cpp (under GameOS/External dependancies) diff --git a/MW4COMPARE/reference/buildmechnames.script b/MW4COMPARE/reference/buildmechnames.script new file mode 100644 index 00000000..9a526a90 --- /dev/null +++ b/MW4COMPARE/reference/buildmechnames.script @@ -0,0 +1,77 @@ +mechnames[ 0 ] = localize$(DNL_ANNIHILATOR) +mechnames[ 1 ] = localize$(DNL_ARCHER) +mechnames[ 2 ] = localize$(DNL_ARCTICWOLF) +mechnames[ 3 ] = localize$(DNL_ARES) +mechnames[ 4 ] = localize$(DNL_ARGUS) +mechnames[ 5 ] = localize$(DNL_ASSASSIN2) +mechnames[ 6 ] = localize$(DNL_ATLAS) +mechnames[ 7 ] = localize$(DNL_AVATAR) +mechnames[ 8 ] = localize$(DNL_AWESOME) +mechnames[ 9 ] = localize$(DNL_BATTLEMASTER) +mechnames[ 10 ] = localize$(DNL_BATTLEMASTERIIC) +mechnames[ 11 ] = localize$(DNL_BEHEMOTH) +mechnames[ 12 ] = localize$(DNL_BEHEMOTHII) +mechnames[ 13 ] = localize$(DNL_BLACKHAWK) +mechnames[ 14 ] = localize$(DNL_BLACKKNIGHT) +mechnames[ 15 ] = localize$(DNL_BLACKLANNER) +mechnames[ 16 ] = localize$(DNL_BRIGAND) +mechnames[ 17 ] = localize$(DNL_BUSHWACKER) +mechnames[ 18 ] = localize$(DNL_CATAPULT) +mechnames[ 19 ] = localize$(DNL_CAULDRONBORN) +mechnames[ 20 ] = localize$(DNL_CHIMERA) +mechnames[ 21 ] = localize$(DNL_COMMANDO) +mechnames[ 22 ] = localize$(DNL_COUGAR) +mechnames[ 23 ] = localize$(DNL_CYCLOPS) +mechnames[ 24 ] = localize$(DNL_DAISHI) +mechnames[ 25 ] = localize$(DNL_DEIMOS) +mechnames[ 26 ] = localize$(DNL_DRAGON) +mechnames[ 27 ] = localize$(DNL_FAFNIR) +mechnames[ 28 ] = localize$(DNL_FLEA) +mechnames[ 29 ] = localize$(DNL_GLADIATOR) +mechnames[ 30 ] = localize$(DNL_GRIZZLY) +mechnames[ 31 ] = localize$(DNL_HAUPTMANN) +mechnames[ 32 ] = localize$(DNL_HELLHOUND) +mechnames[ 33 ] = localize$(DNL_HELLSPAWN) +mechnames[ 34 ] = localize$(DNL_HIGHLANDER) +mechnames[ 35 ] = localize$(DNL_HOLLANDERII) +mechnames[ 36 ] = localize$(DNL_HUNCHBACK) +mechnames[ 37 ] = localize$(DNL_KODIAK) +mechnames[ 38 ] = localize$(DNL_LOKI) +mechnames[ 39 ] = localize$(DNL_LONGBOW) +mechnames[ 40 ] = localize$(DNL_MADCAT) +mechnames[ 41 ] = localize$(DNL_MADCAT2) +mechnames[ 42 ] = localize$(DNL_MASAKARI) +mechnames[ 43 ] = localize$(DNL_MAULER) +mechnames[ 44 ] = localize$(DNL_NOVACAT) +mechnames[ 45 ] = localize$(DNL_OSIRIS) +mechnames[ 46 ] = localize$(DNL_OWENS) +mechnames[ 47 ] = localize$(DNL_PUMA) +mechnames[ 48 ] = localize$(DNL_RAVEN) +mechnames[ 49 ] = localize$(DNL_RIFLEMAN) +mechnames[ 50 ] = localize$(DNL_RYOKEN) +mechnames[ 51 ] = localize$(DNL_SHADOWCAT) +mechnames[ 52 ] = localize$(DNL_SOLITAIRE) +mechnames[ 53 ] = localize$(DNL_SUNDER) +mechnames[ 54 ] = localize$(DNL_TEMPLAR) +mechnames[ 55 ] = localize$(DNL_THANATOS) +mechnames[ 56 ] = localize$(DNL_THOR) +mechnames[ 57 ] = localize$(DNL_ULLER) +mechnames[ 58 ] = localize$(DNL_URBANMECH) +mechnames[ 59 ] = localize$(DNL_UZIEL) +mechnames[ 60 ] = localize$(DNL_VICTOR) +mechnames[ 61 ] = localize$(DNL_VULTURE) +mechnames[ 62 ] = localize$(DNL_WARHAMMER) +mechnames[ 63 ] = localize$(DNL_WOLFHOUND) +mechnames[ 64 ] = localize$(DNL_ZEUS) +mechnames[ 65 ] = localize$(DNL_CHAMPION) +mechnames[ 66 ] = localize$(DNL_JENNER2C) +mechnames[ 67 ] = localize$(DNL_DASHER) +mechnames[ 68 ] = localize$(DNL_MARAUDER) +mechnames[ 69 ] = localize$(DNL_THUNDERBOLT) +mechnames[ 70 ] = localize$(DNL_GRIFFIN) +//mechnames[ 70 ] = localize$(IDS_ML_NA_INFO) +//mechnames[ 71 ] = localize$(IDS_ML_NA_INFO) + + + + diff --git a/MW4COMPARE/reference/mechnames.script b/MW4COMPARE/reference/mechnames.script new file mode 100644 index 00000000..96f2f59b --- /dev/null +++ b/MW4COMPARE/reference/mechnames.script @@ -0,0 +1,77 @@ +mech_name[ 0 ] = localize$(DNL_ANNIHILATOR) +mech_name[ 1 ] = localize$(DNL_ARCHER) +mech_name[ 2 ] = localize$(DNL_ARCTICWOLF) +mech_name[ 3 ] = localize$(DNL_ARES) +mech_name[ 4 ] = localize$(DNL_ARGUS) +mech_name[ 5 ] = localize$(DNL_ASSASSIN2) +mech_name[ 6 ] = localize$(DNL_ATLAS) +mech_name[ 7 ] = localize$(DNL_AVATAR) +mech_name[ 8 ] = localize$(DNL_AWESOME) +mech_name[ 9 ] = localize$(DNL_BATTLEMASTER) +mech_name[ 10 ] = localize$(DNL_BATTLEMASTERIIC) +mech_name[ 11 ] = localize$(DNL_BEHEMOTH) +mech_name[ 12 ] = localize$(DNL_BEHEMOTHII) +mech_name[ 13 ] = localize$(DNL_BLACKHAWK) +mech_name[ 14 ] = localize$(DNL_BLACKKNIGHT) +mech_name[ 15 ] = localize$(DNL_BLACKLANNER) +mech_name[ 16 ] = localize$(DNL_BRIGAND) +mech_name[ 17 ] = localize$(DNL_BUSHWACKER) +mech_name[ 18 ] = localize$(DNL_CATAPULT) +mech_name[ 19 ] = localize$(DNL_CAULDRONBORN) +mech_name[ 20 ] = localize$(DNL_CHIMERA) +mech_name[ 21 ] = localize$(DNL_COMMANDO) +mech_name[ 22 ] = localize$(DNL_COUGAR) +mech_name[ 23 ] = localize$(DNL_CYCLOPS) +mech_name[ 24 ] = localize$(DNL_DAISHI) +mech_name[ 25 ] = localize$(DNL_DEIMOS) +mech_name[ 26 ] = localize$(DNL_DRAGON) +mech_name[ 27 ] = localize$(DNL_FAFNIR) +mech_name[ 28 ] = localize$(DNL_FLEA) +mech_name[ 29 ] = localize$(DNL_GLADIATOR) +mech_name[ 30 ] = localize$(DNL_GRIZZLY) +mech_name[ 31 ] = localize$(DNL_HAUPTMANN) +mech_name[ 32 ] = localize$(DNL_HELLHOUND) +mech_name[ 33 ] = localize$(DNL_HELLSPAWN) +mech_name[ 34 ] = localize$(DNL_HIGHLANDER) +mech_name[ 35 ] = localize$(DNL_HOLLANDERII) +mech_name[ 36 ] = localize$(DNL_HUNCHBACK) +mech_name[ 37 ] = localize$(DNL_KODIAK) +mech_name[ 38 ] = localize$(DNL_LOKI) +mech_name[ 39 ] = localize$(DNL_LONGBOW) +mech_name[ 40 ] = localize$(DNL_MADCAT) +mech_name[ 41 ] = localize$(DNL_MADCAT2) +mech_name[ 42 ] = localize$(DNL_MASAKARI) +mech_name[ 43 ] = localize$(DNL_MAULER) +mech_name[ 44 ] = localize$(DNL_NOVACAT) +mech_name[ 45 ] = localize$(DNL_OSIRIS) +mech_name[ 46 ] = localize$(DNL_OWENS) +mech_name[ 47 ] = localize$(DNL_PUMA) +mech_name[ 48 ] = localize$(DNL_RAVEN) +mech_name[ 49 ] = localize$(DNL_RIFLEMAN) +mech_name[ 50 ] = localize$(DNL_RYOKEN) +mech_name[ 51 ] = localize$(DNL_SHADOWCAT) +mech_name[ 52 ] = localize$(DNL_SOLITAIRE) +mech_name[ 53 ] = localize$(DNL_SUNDER) +mech_name[ 54 ] = localize$(DNL_TEMPLAR) +mech_name[ 55 ] = localize$(DNL_THANATOS) +mech_name[ 56 ] = localize$(DNL_THOR) +mech_name[ 57 ] = localize$(DNL_ULLER) +mech_name[ 58 ] = localize$(DNL_URBANMECH) +mech_name[ 59 ] = localize$(DNL_UZIEL) +mech_name[ 60 ] = localize$(DNL_VICTOR) +mech_name[ 61 ] = localize$(DNL_VULTURE) +mech_name[ 62 ] = localize$(DNL_WARHAMMER) +mech_name[ 63 ] = localize$(DNL_WOLFHOUND) +mech_name[ 64 ] = localize$(DNL_ZEUS) +mech_name[ 65 ] = localize$(DNL_CHAMPION) +mech_name[ 66 ] = localize$(DNL_JENNER2C) +mech_name[ 67 ] = localize$(DNL_DASHER) +mech_name[ 68 ] = localize$(DNL_MARAUDER) +mech_name[ 69 ] = localize$(DNL_THUNDERBOLT) +mech_name[ 70 ] = localize$(DNL_GRIFFIN) +mech_name[ 71 ] = localize$(IDS_ML_NA_INFO) +mech_name[ 72 ] = localize$(IDS_ML_NA_INFO) + + + + diff --git a/MW4COMPARE/reference/mwconst.abi.diff b/MW4COMPARE/reference/mwconst.abi.diff new file mode 100644 index 00000000..a06bdeab --- /dev/null +++ b/MW4COMPARE/reference/mwconst.abi.diff @@ -0,0 +1,24 @@ +538,539c538,557 +< LastMechID = 64 ; +< NoMechID = 65 ; +--- +> M_Champion = 65 ; +> M_Jenner2c = 66 ; +> M_Dasher = 67 ; +> M_Marauder = 68 ; +> M_Thunderbolt = 69 ; +> M_Griffin = 70 ; +> LastMechID = 70 ; +> NoMechID = 71 ; +> +> +> //sip +> //not needed here +> //M_CameraShip = LastMechID+1; +> //EmptyMechID = LastMechID+2; +> //sip +> //sip +> LastMP1MechID = M_Solitaire; +> +> // +> diff --git a/MW4COMPARE/reference/options.ini.diff b/MW4COMPARE/reference/options.ini.diff new file mode 100644 index 00000000..a7304be6 --- /dev/null +++ b/MW4COMPARE/reference/options.ini.diff @@ -0,0 +1,90 @@ +6,7c6,7 +< playericon=Content\Textures\stockdecals\decal_48.tga +< teamicon=Content\Textures\stockdecals\decal_48.tga +--- +> playericon=Content\Textures\stockdecals\decal_49.tga +> teamicon=Content\Textures\stockdecals\decal_50.tga +29c29 +< bitdepth=16 +--- +> bitdepth=32 +56c56 +< visibility=1 +--- +> visibility=0 +60c60 +< heaton=0 +--- +> heaton=1 +68,69c68,69 +< unlimitedammo=1 +< friendlyfirepercentage=0 +--- +> unlimitedammo=0 +> friendlyfirepercentage=100 +79c79 +< ruletype=2 +--- +> ruletype=17 +84c84 +< allowedbeam1=ffffffff +--- +> allowedbeam1=fffffffd +86c86 +< allowedmissile1=ffffffff +--- +> allowedmissile1=ff3bffff +88c88 +< allowedprojectile1=ffffffff +--- +> allowedprojectile1=ffffdfff +112c112 +< lrpt=072003082ab4b906fabd1c154d3d8fd77942028e +--- +> lrpt=032020102ab4b906fabd1c154d3d8fd77942028e +115,118c115 +< RuleBook=1 +< DawnWar=1 +< BiggieSizeIt=1 +< CanYouHearTheFootSteps=1 +--- +> +144,181d140 +< +< [RookieMission] +< // Default mission loaded when the console opens and when "Default" is clicked. +< // All entries are optional - omit any to keep the hardcoded built-in default. +< // +< // MissionName - exact display name as it appears in the map dropdown +< // default: ScarabStronghold - Attrition +< // GameType - 0-based index into the game-type dropdown (2 = Attrition) +< // default: 2 +< // TimeLimit - time limit in minutes; -1 means use the server's default time +< // default: -1 (uses g_nTimeList_Value, currently 7 min) +< // Visibility - 0=Clear 1=Light Fog 2=Medium Fog 3=Heavy Fog +< // Weather - 0=Off 1=Rain +< // TimeOfDay - 0=Day 1=Night +< // Radar - 0=Novice 1=Off 2=Bars 3=Unlimited +< // HeatOn - 0=Off 1=On +< // FriendlyFire - 0=Off 100=Full (percentage) +< // SplashDamage - 0=Off 1=On +< // UnlimitedAmmo - 0=Off 1=On +< // WeaponJam - 0=Off 1=On +< // AdvanceMode - 0=Off 1=On +< // ArmorMode - 0=Off 1=On +< // +< // Example: uncomment and edit lines below to customize +< //MissionName=ScarabStronghold - Attrition +< //GameType=2 +< //TimeLimit=-1 +< //Visibility=0 +< //Weather=0 +< //TimeOfDay=0 +< //Radar=0 +< //HeatOn=0 +< //FriendlyFire=0 +< //SplashDamage=0 +< //UnlimitedAmmo=1 +< //WeaponJam=0 +< //AdvanceMode=0 +< //ArmorMode=0 diff --git a/MW4COMPARE/reference/v4h-changelog.txt b/MW4COMPARE/reference/v4h-changelog.txt new file mode 100644 index 00000000..1b631cb4 --- /dev/null +++ b/MW4COMPARE/reference/v4h-changelog.txt @@ -0,0 +1,117 @@ + +Firestorm Battletech SMT Build Four(v4h). + +This is a more secure release build and has been tested with about 80 variants on the extra models. + +Controls defaults reset. Menu updated again. Skins amended and should be duplicate free. + +The six hard to kill mechs are added and stable.The base/stock variant will not have and advanced +gyro as a starting addition. All weapons sites are based on mech design and are fixed as displayed. +The ammo limit continues to be set at three tons per weapon maximum. This ensures balance and resolves conflicts. + +Restrictions on the new mechs are fixed and if the model can't accomodate a function it will no be +added. + +Updated maps within Solaris will contain the occasional drop out of voice sounds.Live with it!. Some of the mech generic sounds overide additional voices in the pods. Live with it. +Any music will only play in the Server pod. All additinal audio will continue to work as normal. + +Tested in the stock build with up to Six Pilots with no issues. + + + + +Firestorm Battletech SMT Build Four(v4f). + +Important. +All spinning mechs displayed on the Secondary screen pre mission are accurate. The main details below the mech are correct except class and type. If the spinning model is not the same model as chosen from the console the build has been corrupted. This can happen when variants are saved and during game crashes. + +When adding new variants you must test each new variant as a corrupted variant can corrupt the build. +Do create new variants on the new build but you must check they do not corrupt the build when added. +You are advised to create new variants for the new mechs on the newest build only. Test as you go. +Save the new variants seperately once tested so you can add again if you get a corrupted build. + +one additional five level mech added. This a std chassis and does not include Jumpjets options for this model.No animation for Jump jets applicable for this model. +Mech lab is still as issue for crashes when saving use scroll lock to break out.Watch for corrupt variants +The hsh folder has been archived as hshold. +The new hsh folder is a compromise for the new mechs to standardise the block hud display. +Huds are not amended use the bar chart huds for models which are accurate. +All printouts are accurate for mechs including new versions. + +Amended Hud and new Mech tested on Eighty plus test runs.Stock and Variants with no problems. + + + + + + +Firestorm Battletech SMT Build Four(v4e). + +one additional five level mech added. +Mech lab is still as issue for crashes when saving use scroll lock to break out. +This is still the best way to exit a crash and will help protect against gpu/cpu failures. +Additional stock mechs are limited to 1990 basic technical stats as standard. +Huds are not amended use the bar chart huds for models which are accurate. +All new mechs use low level block display. +All printouts are accurate for new mechs. +Main view display will show all new mechs as Unclassified. +So you can ignore technical info on the Main view screen pre game launch. +Game lag limited for additional models. +Actual lag in each pod will be dependent on the pea sized cpu/gpu running on your pod. +New mechs should have better starting groups for weapons pre set. +Any new mech will not have rear facing weapons. + + + +Firestorm Battletech SMT Build Four(v4d). + +one additional high level mech added. +Subsystems error amended to limit crashes in game with high level LOD. +Mech lab is still an issue for crashes when saving use scroll lock to break out. +Huds are not amended use the bar chart huds for models. +All printouts are accurate. +Main view display correct model but will show parent mech details.So ignore technical info on the Main view screen pre game launch. + + + +Firestorm Battletech SMT Build Four(v4c) + +one additional mech added . Huds and displays will still not match correct mech visuals. This is an index link issue . The additional mech is the only point of this update to test the high level lod on the erf in the pod to see how much lag there is in the pods. + +It is best to simply amend your huds to the bar chart hud in this build as it always shows the correct damage. + +Still be aware of the mech lab crash always use scroll lock to exit when this crashes/freezes this will help minimise any problems with your gpu caused by this type of crash. + + + + +Firestorm Battletech SMT Build Four(v4) + + +MechLab. + + +Weapon box sizes set to minimum size to show more of the background mech being edited. Special Weapon boxes locations have been clearly indicated on the chassis weapon tab and not simply mixed with other weapon boxes. If they don't have a special displayed in the mech lab the +model does not have one. Weapon ammo is limited to no more than three tons for every individual weapon. Example Ac10 cannot mount more than three tons of ammo . All mechs can be created from the new mech button and the subsequent drop down box. These will all show in the main mech lab +after they have been created and can be edited from here. The Battlemaster and Behmoth will only display in the mechlab list once created but both versions will show in that drop down. So behemoth,behemothII and all there variants can be edited from the parent mech. + +All mechs are playable in both Dragon Multiplayer and Console Multiplayer. There are also no conflicts with printed reports on mission with the correct mech represented with each report. This has been tested heavily. The hooks for additional mechs are in place. + +There is a specific crash within the build while working within the mech lab . This crash occurs most often when you try to save a mech when in the group tab. When editing a mech, less crashes occur when saving from the chassis tab and not the weapon tab. Should there be a crash the scroll lockkey will exit the crash. Best way to exit a crash in this GOS build. + +Callsigns. + +Callsigns within the Dragon multiplayer can be amended within the firestorm.h file. The length of the Player name needs to be shorter than twelve characters to not interfere with scoring. Callsigns within the console cannot be edited from this file. they can only be edited in the code +or when built from source. There are a number of changes in the callsigns for console multiplayer. e g Blackthorn, Sipstrassi and names with all Capital letters removed. + + +Hud displays in Dragon multiplayer will not reflect the mech actually being used on all occasions. But reflect the chassis used for this.Its a compromise based on the HSH limits. + + + + + + + + + + diff --git a/MW4COMPARE/reference/v4h-firestorm.h b/MW4COMPARE/reference/v4h-firestorm.h new file mode 100644 index 00000000..f4219ee8 --- /dev/null +++ b/MW4COMPARE/reference/v4h-firestorm.h @@ -0,0 +1,52 @@ +#define NUM_CALLSIGNS 20 +//These need to cover the full range of levels 0 to 9 at least one of each +//Twelve Characters at the most please as the length is apparent on scoreboard +callsigns +{ + GUI_CREATE + { + string names[NUM_CALLSIGNS] + int levels[NUM_CALLSIGNS] + + names[0] = "Vlad Ward" + names[1] = "Blackthorns" + names[2] = "Phelan Kell" + names[3] = "Morgan Kell" + names[4] = "Vance Rezak" + names[5] = "T. Sandoval" + names[6] = "Jerimiah Rose" + names[7] = "Sun Liao" + names[8] = "A. Focht" + names[9] = "Sipstrassi" + names[10] = "Diana Pryde" + names[11] = "Sturm Kintaro" + names[12] = "Angela Bekker" + names[13] = "Constant Tseng" + names[14] = "Tamoe Sakade" + names[15] = "Theo Kurita" + names[16] = "Shin Yodama" + names[17] = "Maeve Wolf" + names[18] = "Kael Pershaw" + names[19] = "Salome Ward" + levels[0] = 8 + levels[1] = 9 + levels[2] = 8 + levels[3] = 8 + levels[4] = 5 + levels[5] = 9 + levels[6] = 5 + levels[7] = 7 + levels[8] = 9 + levels[9] = 8 + levels[10] = 9 + levels[11] = 9 + levels[12] = 8 + levels[13] = 0 + levels[14] = 1 + levels[15] = 6 + levels[16] = 5 + levels[17] = 4 + levels[18] = 3 + levels[19] = 2 + } +} diff --git a/MW4COMPARE/reference/v4h-firestorm64.h b/MW4COMPARE/reference/v4h-firestorm64.h new file mode 100644 index 00000000..1ef7cb73 --- /dev/null +++ b/MW4COMPARE/reference/v4h-firestorm64.h @@ -0,0 +1,144 @@ +#define NUM_CALLSIGNS 64 + +//These need to cover the full range of levels 0 to 9 at least one of each +//Twelve Characters at the most please. + +// If you update the names here, please update Code\MW4\MW4Shell.cpp as well! Just search for NUM_CALLSIGNS. +callsigns +{ + GUI_CREATE + { + string names[NUM_CALLSIGNS] + int levels[NUM_CALLSIGNS] + + names[0] = "Sipstrassi" + names[1] = "Scarab" + names[2] = "Pocus" + names[3] = "Hacksaw" + names[4] = "Gnaw" + names[5] = "Darklord" + names[6] = "Spice" + names[7] = "Rocker" + names[8] = "Leepus" + names[9] = "Coolhand" + names[10] = "Piotr" + names[11] = "Propwash" + names[12] = "Mecca" + names[13] = "Rusty" + names[14] = "Faith" + names[15] = "Thomcat" + names[16] = "Anubis" + names[17] = "Tryptic" + names[18] = "Isis" + names[19] = "Suredude" + names[20] = "Jerimiah Rose" + names[21] = "Lucky" + names[22] = "Gothmog" + names[23] = "Grayson" + names[24] = "Arioch" + names[25] = "Blackthorns" + names[26] = "Splotch" + names[27] = "Paladin" + names[28] = "Macleod" + names[29] = "ArcLight" + names[30] = "BooYah" + names[31] = "Von" + names[32] = "Azero" + names[33] = "Wraith" + names[34] = "Undead" + names[35] = "Mantis" + names[36] = "Wicced" + names[37] = "Tiny" + names[38] = "Crusher" + names[39] = "Darkman" + names[40] = "Oxen" + names[41] = "Morfane" + names[42] = "Darkheart" + names[43] = "Joker" + names[44] = "Mouse" + names[45] = "Greywolf" + names[46] = "Pyrotech" + names[47] = "Elengil" + names[48] = "Undomiel" + names[49] = "Marz" + names[50] = "Kuroshii" + names[51] = "Crow" + names[52] = "Con" + names[53] = "Lysosome" + names[54] = "Dax" + names[55] = "HamHouke" + names[56] = "Intrepid" + names[57] = "Toad" + names[58] = "Asterix" + names[59] = "Apophis" + names[60] = "Thor" + names[61] = "Sauron" + names[62] = "Pharaoh" + names[63] = "Dicion" + + levels[0] = 1 + levels[1] = 7 + levels[2] = 6 + levels[3] = 8 + levels[4] = 6 + levels[5] = 7 + levels[6] = 7 + levels[7] = 2 + levels[8] = 5 + levels[9] = 6 + levels[10] = 7 + levels[11] = 8 + levels[12] = 5 + levels[13] = 2 + levels[14] = 7 + levels[15] = 4 + levels[16] = 3 + levels[17] = 2 + levels[18] = 4 + levels[19] = 7 + levels[20] = 2 + levels[21] = 2 + levels[22] = 4 + levels[23] = 7 + levels[24] = 4 + levels[25] = 2 + levels[26] = 5 + levels[27] = 6 + levels[28] = 7 + levels[29] = 8 + levels[30] = 8 + levels[31] = 6 + levels[32] = 2 + levels[33] = 2 + levels[34] = 5 + levels[35] = 6 + levels[36] = 3 + levels[37] = 2 + levels[38] = 7 + levels[39] = 6 + levels[40] = 7 + levels[41] = 6 + levels[42] = 2 + levels[43] = 6 + levels[44] = 5 + levels[45] = 6 + levels[46] = 8 + levels[47] = 7 + levels[48] = 5 + levels[49] = 6 + levels[50] = 5 + levels[51] = 7 + levels[52] = 3 + levels[53] = 5 + levels[54] = 4 + levels[55] = 4 + levels[56] = 4 + levels[57] = 4 + levels[58] = 4 + levels[59] = 4 + levels[60] = 4 + levels[61] = 8 + levels[62] = 7 + levels[63] = 0 + } +} diff --git a/MW4COMPARE/reference/v4h-options.h b/MW4COMPARE/reference/v4h-options.h new file mode 100644 index 00000000..f8a39d45 --- /dev/null +++ b/MW4COMPARE/reference/v4h-options.h @@ -0,0 +1,54 @@ +//THIS FILE CONTAINS MERCS CLIENT CONFIGURABLE OPTIONS +// DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING + +//************************************************************ +// set SHOW_TIME to 'true' to enable the wall clock, 'false' +// to disable it. +#define SHOW_TIME false + + + + +//************************************************************ +//sip // 1 is firestorm standard and there is no zeus true +//sip // 0 is standard mech list including zeus false +#define USE_ALLOWED_MECHS 1 + + +//************************************************************ +// set Battle_Ammo to 'true' to enable the ammo limit per weapon, 'false' +// to disable it. +// max three ton per weapon as per mech space specification. +#define Battle_Ammo true + + +//************************************************************ +// set SERVER_DETAIL to 'true' to enable server details +// in the Server Lobby, 'false' to disable it. +#define SERVER_DETAIL false + +//************************************************************ +// set this value to 'true' to enable observer mode, or +// 'false' to disable it. +#define USE_OBSERVER true + +//************************************************************ +// set this value to how many seconds to wait before +// auto-launching into camera mode. Setting to 15 or 20 seconds +// is a good value. +#define OBSERVER_WAIT_TIMEOUT 15 + +//************************************************************ +// setting this value sets the base priority you want the +// game to run at. Note, this only has an effect on WinXP and +// Win2k systems. You must have one of these selected regardless. +//#define BASE_PRIORITY "BELOW_NORMAL" +#define BASE_PRIORITY "NORMAL" +//#define BASE_PRIORITY "ABOVE_NORMAL" + +//************************************************************ + +// This is the music files you wish to loop. It must be in +// .wav format. +#define SHELL_MUSIC_FILENAME "mechevolution.wav" +//#define SHELL_MUSIC_FILENAME "nextmove_music.wav" diff --git a/MW4COMPARE/reports/.gitignore b/MW4COMPARE/reports/.gitignore new file mode 100644 index 00000000..ecccb4b0 --- /dev/null +++ b/MW4COMPARE/reports/.gitignore @@ -0,0 +1,4 @@ +# Regenerate everything with ../run-comparison.sh +# The two manifest-*.tsv files are ~5 MB each and are intermediate build products. +manifest-*.tsv +manifest-*.err diff --git a/MW4COMPARE/reports/content-diff.txt b/MW4COMPARE/reports/content-diff.txt new file mode 100644 index 00000000..0c639abb --- /dev/null +++ b/MW4COMPARE/reports/content-diff.txt @@ -0,0 +1,13 @@ +# V4H=14 files, OURS=46074 files | identical=2 differ=1 V4H_only=11 OURS_only=46071 ++V4H shellscripts/clock.script 988 ++V4H shellscripts/cm_listboxes.script 18427 ++V4H shellscripts/firestorm.h 1207 ++V4H shellscripts/firestorm64.h 3052 ++V4H shellscripts/globals.script 469 ++V4H shellscripts/graphics/multiplayer/lobbydecals/decal_.tga 4140 ++V4H shellscripts/mechbay/buildmechnames.script 3112 ++V4H shellscripts/mechbay/mechdescriptions.script 74883 ++V4H shellscripts/mechbay/mechnames.script 3108 ++V4H shellscripts/mechbay/old mechdescriptions.script 74438 ++V4H shellscripts/options.h 2011 +~DIF ABLscripts/mwconst.abi V4H=26905B OURS=26643B diff --git a/MW4COMPARE/reports/core-decoded-diff.txt b/MW4COMPARE/reports/core-decoded-diff.txt new file mode 100644 index 00000000..ba776d22 --- /dev/null +++ b/MW4COMPARE/reports/core-decoded-diff.txt @@ -0,0 +1,1188 @@ +# entries: A=12709 B=11955 A_only=754 B_only=0 decoded-differ=433 ++A mechs/champion/armaturedata/chp_hip.data 12B ++A mechs/champion/armaturedata/chp_hip.data{element} 100B ++A mechs/champion/armaturedata/chp_hip.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_lbtoe.data 12B ++A mechs/champion/armaturedata/chp_lbtoe.data{element} 100B ++A mechs/champion/armaturedata/chp_lbtoe.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_ldleg.data 12B ++A mechs/champion/armaturedata/chp_ldleg.data{element} 100B ++A mechs/champion/armaturedata/chp_ldleg.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_lfoot.data 12B ++A mechs/champion/armaturedata/chp_lfoot.data{element} 100B ++A mechs/champion/armaturedata/chp_lfoot.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_lftoe.data 12B ++A mechs/champion/armaturedata/chp_lftoe.data{element} 100B ++A mechs/champion/armaturedata/chp_lftoe.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_lgun.data 12B ++A mechs/champion/armaturedata/chp_lgun.data{element} 100B ++A mechs/champion/armaturedata/chp_lgun.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_luarm.data 12B ++A mechs/champion/armaturedata/chp_luarm.data{element} 100B ++A mechs/champion/armaturedata/chp_luarm.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_luleg.data 12B ++A mechs/champion/armaturedata/chp_luleg.data{element} 100B ++A mechs/champion/armaturedata/chp_luleg.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_rbtoe.data 12B ++A mechs/champion/armaturedata/chp_rbtoe.data{element} 100B ++A mechs/champion/armaturedata/chp_rbtoe.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_rdleg.data 12B ++A mechs/champion/armaturedata/chp_rdleg.data{element} 100B ++A mechs/champion/armaturedata/chp_rdleg.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_rfoot.data 12B ++A mechs/champion/armaturedata/chp_rfoot.data{element} 100B ++A mechs/champion/armaturedata/chp_rfoot.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_rftoe.data 12B ++A mechs/champion/armaturedata/chp_rftoe.data{element} 100B ++A mechs/champion/armaturedata/chp_rftoe.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_rgun.data 12B ++A mechs/champion/armaturedata/chp_rgun.data{element} 100B ++A mechs/champion/armaturedata/chp_rgun.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_ruarm.data 12B ++A mechs/champion/armaturedata/chp_ruarm.data{element} 100B ++A mechs/champion/armaturedata/chp_ruarm.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_ruleg.data 12B ++A mechs/champion/armaturedata/chp_ruleg.data{element} 100B ++A mechs/champion/armaturedata/chp_ruleg.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_specialone.data 12B ++A mechs/champion/armaturedata/chp_specialone.data{element} 100B ++A mechs/champion/armaturedata/chp_specialone.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_specialtwo.data 12B ++A mechs/champion/armaturedata/chp_specialtwo.data{element} 100B ++A mechs/champion/armaturedata/chp_specialtwo.data{gamemodel} 80B ++A mechs/champion/armaturedata/chp_torso.data 12B ++A mechs/champion/armaturedata/chp_torso.data{element} 100B ++A mechs/champion/armaturedata/chp_torso.data{gamemodel} 80B ++A mechs/champion/armaturedata/joint_cage.data 12B ++A mechs/champion/armaturedata/joint_cage.data{element} 100B ++A mechs/champion/armaturedata/joint_cage.data{gamemodel} 80B ++A mechs/champion/armaturevideo/chp_hip.video 406B ++A mechs/champion/armaturevideo/chp_lbtoe.video 406B ++A mechs/champion/armaturevideo/chp_ldleg.video 406B ++A mechs/champion/armaturevideo/chp_lfoot.video 406B ++A mechs/champion/armaturevideo/chp_lftoe.video 406B ++A mechs/champion/armaturevideo/chp_lgun.video 285B ++A mechs/champion/armaturevideo/chp_luarm.video 406B ++A mechs/champion/armaturevideo/chp_luleg.video 406B ++A mechs/champion/armaturevideo/chp_rbtoe.video 406B ++A mechs/champion/armaturevideo/chp_rdleg.video 406B ++A mechs/champion/armaturevideo/chp_rfoot.video 406B ++A mechs/champion/armaturevideo/chp_rftoe.video 406B ++A mechs/champion/armaturevideo/chp_rgun.video 285B ++A mechs/champion/armaturevideo/chp_ruarm.video 406B ++A mechs/champion/armaturevideo/chp_ruleg.video 406B ++A mechs/champion/armaturevideo/chp_specialone.video 406B ++A mechs/champion/armaturevideo/chp_specialtwo.video 406B ++A mechs/champion/armaturevideo/chp_torso.video 683B ++A mechs/champion/armaturevideo/joint_cage.video 646B ++A mechs/champion/champion.contents 282B ++A mechs/champion/champion.contents[joint_cage]{sites} 124B ++A mechs/champion/champion.contents[joint_hip]{armature} 282B ++A mechs/champion/champion.contents[joint_hipabove]{armature} 282B ++A mechs/champion/champion.contents[joint_hipbelow]{armature} 282B ++A mechs/champion/champion.contents[joint_lankle]{armature} 282B ++A mechs/champion/champion.contents[joint_lbelowankle]{armature} 842B ++A mechs/champion/champion.contents[joint_lbelowankle]{sites} 39B ++A mechs/champion/champion.contents[joint_ldleg]{armature} 282B ++A mechs/champion/champion.contents[joint_ldleg]{sites} 43B ++A mechs/champion/champion.contents[joint_lefttorsofront]{sites} 44B ++A mechs/champion/champion.contents[joint_lgun]{sites} 86B ++A mechs/champion/champion.contents[joint_lgunabove]{armature} 282B ++A mechs/champion/champion.contents[joint_luarm]{armature} 282B ++A mechs/champion/champion.contents[joint_luleg]{armature} 282B ++A mechs/champion/champion.contents[joint_rankle]{armature} 282B ++A mechs/champion/champion.contents[joint_rbelowankle]{armature} 842B ++A mechs/champion/champion.contents[joint_rbelowankle]{sites} 39B ++A mechs/champion/champion.contents[joint_rdleg]{armature} 282B ++A mechs/champion/champion.contents[joint_rdleg]{sites} 43B ++A mechs/champion/champion.contents[joint_rgun]{sites} 130B ++A mechs/champion/champion.contents[joint_rgunabove]{armature} 282B ++A mechs/champion/champion.contents[joint_righttorsofront]{sites} 44B ++A mechs/champion/champion.contents[joint_root]{armature} 842B ++A mechs/champion/champion.contents[joint_ruarm]{armature} 282B ++A mechs/champion/champion.contents[joint_ruleg]{armature} 282B ++A mechs/champion/champion.contents[joint_specialone]{sites} 46B ++A mechs/champion/champion.contents[joint_specialtwo]{sites} 46B ++A mechs/champion/champion.contents[joint_torso]{armature} 282B ++A mechs/champion/champion.contents[joint_torsoabove]{armature} 3082B ++A mechs/champion/champion.contents[joint_torsoabove]{sites} 521B ++A mechs/champion/champion.contents[joint_torsobelow]{armature} 282B ++A mechs/champion/champion.contents[joint_vel]{armature} 282B ++A mechs/champion/champion.contents[joint_world]{armature} 282B ++A mechs/champion/champion.contents[joint_world]{sites} 40B ++A mechs/champion/champion.damage 1688B ++A mechs/champion/champion.data 12B ++A mechs/champion/champion.data[shadow] 117B ++A mechs/champion/champion.data{element} 100B ++A mechs/champion/champion.data{footsteps} 96B ++A mechs/champion/champion.data{gamemodel} 1636B ++A mechs/champion/champion.data{hierarchicalobb} 5607B ++A mechs/champion/champion.data{solidobb} 78B ++A mechs/champion/champion.engine 12B ++A mechs/champion/champion.engine{element} 100B ++A mechs/champion/champion.engine{gamemodel} 64B ++A mechs/champion/champion.instance 344B ++A mechs/champion/champion.subsystems 4698B ++A mechs/champion/champion.torso 12B ++A mechs/champion/champion.torso{element} 100B ++A mechs/champion/champion.torso{gamemodel} 1608B ++A mechs/champion_destroyed/champion_stroyed.data 12B ++A mechs/champion_destroyed/champion_stroyed.data{element} 100B ++A mechs/champion_destroyed/champion_stroyed.data{gamemodel} 32B ++A mechs/champion_destroyed/champion_stroyed.video 285B ++A mechs/champion_destroyed/champion_stroyed_solid.obb 78B ++A mechs/dasher/armaturedata/das_hip.data 12B ++A mechs/dasher/armaturedata/das_hip.data{element} 100B ++A mechs/dasher/armaturedata/das_hip.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_ldleg.data 12B ++A mechs/dasher/armaturedata/das_ldleg.data{element} 100B ++A mechs/dasher/armaturedata/das_ldleg.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_lfoot.data 12B ++A mechs/dasher/armaturedata/das_lfoot.data{element} 100B ++A mechs/dasher/armaturedata/das_lfoot.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_lgun.data 12B ++A mechs/dasher/armaturedata/das_lgun.data{element} 100B ++A mechs/dasher/armaturedata/das_lgun.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_ltoe.data 12B ++A mechs/dasher/armaturedata/das_ltoe.data{element} 100B ++A mechs/dasher/armaturedata/das_ltoe.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_luarm.data 12B ++A mechs/dasher/armaturedata/das_luarm.data{element} 100B ++A mechs/dasher/armaturedata/das_luarm.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_luleg.data 12B ++A mechs/dasher/armaturedata/das_luleg.data{element} 100B ++A mechs/dasher/armaturedata/das_luleg.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_rdleg.data 12B ++A mechs/dasher/armaturedata/das_rdleg.data{element} 100B ++A mechs/dasher/armaturedata/das_rdleg.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_rfoot.data 12B ++A mechs/dasher/armaturedata/das_rfoot.data{element} 100B ++A mechs/dasher/armaturedata/das_rfoot.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_rgun.data 12B ++A mechs/dasher/armaturedata/das_rgun.data{element} 100B ++A mechs/dasher/armaturedata/das_rgun.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_rtoe.data 12B ++A mechs/dasher/armaturedata/das_rtoe.data{element} 100B ++A mechs/dasher/armaturedata/das_rtoe.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_ruarm.data 12B ++A mechs/dasher/armaturedata/das_ruarm.data{element} 100B ++A mechs/dasher/armaturedata/das_ruarm.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_ruleg.data 12B ++A mechs/dasher/armaturedata/das_ruleg.data{element} 100B ++A mechs/dasher/armaturedata/das_ruleg.data{gamemodel} 80B ++A mechs/dasher/armaturedata/das_torso.data 12B ++A mechs/dasher/armaturedata/das_torso.data{element} 100B ++A mechs/dasher/armaturedata/das_torso.data{gamemodel} 80B ++A mechs/dasher/armaturedata/joint_cage.data 12B ++A mechs/dasher/armaturedata/joint_cage.data{element} 100B ++A mechs/dasher/armaturedata/joint_cage.data{gamemodel} 80B ++A mechs/dasher/armaturevideo/das_hip.video 406B ++A mechs/dasher/armaturevideo/das_ldleg.video 406B ++A mechs/dasher/armaturevideo/das_lfoot.video 406B ++A mechs/dasher/armaturevideo/das_lgun.video 406B ++A mechs/dasher/armaturevideo/das_ltoe.video 406B ++A mechs/dasher/armaturevideo/das_luarm.video 406B ++A mechs/dasher/armaturevideo/das_luleg.video 406B ++A mechs/dasher/armaturevideo/das_rdleg.video 406B ++A mechs/dasher/armaturevideo/das_rfoot.video 406B ++A mechs/dasher/armaturevideo/das_rgun.video 285B ++A mechs/dasher/armaturevideo/das_rtoe.video 406B ++A mechs/dasher/armaturevideo/das_ruarm.video 406B ++A mechs/dasher/armaturevideo/das_ruleg.video 406B ++A mechs/dasher/armaturevideo/das_torso.video 683B ++A mechs/dasher/armaturevideo/joint_cage.video 646B ++A mechs/dasher/dasher.contents 282B ++A mechs/dasher/dasher.contents[joint_cage]{sites} 124B ++A mechs/dasher/dasher.contents[joint_head]{sites} 41B ++A mechs/dasher/dasher.contents[joint_hip]{armature} 282B ++A mechs/dasher/dasher.contents[joint_hipabove]{armature} 282B ++A mechs/dasher/dasher.contents[joint_hipbelow]{armature} 282B ++A mechs/dasher/dasher.contents[joint_lankle]{armature} 282B ++A mechs/dasher/dasher.contents[joint_lbelowankle]{armature} 562B ++A mechs/dasher/dasher.contents[joint_lbelowankle]{sites} 39B ++A mechs/dasher/dasher.contents[joint_ldleg]{armature} 282B ++A mechs/dasher/dasher.contents[joint_lefttorsofront]{sites} 44B ++A mechs/dasher/dasher.contents[joint_lgun]{sites} 172B ++A mechs/dasher/dasher.contents[joint_lgunabove]{armature} 282B ++A mechs/dasher/dasher.contents[joint_luarm]{armature} 282B ++A mechs/dasher/dasher.contents[joint_luleg]{armature} 282B ++A mechs/dasher/dasher.contents[joint_rankle]{armature} 282B ++A mechs/dasher/dasher.contents[joint_rbelowankle]{armature} 562B ++A mechs/dasher/dasher.contents[joint_rbelowankle]{sites} 39B ++A mechs/dasher/dasher.contents[joint_rdleg]{armature} 282B ++A mechs/dasher/dasher.contents[joint_rgun]{sites} 172B ++A mechs/dasher/dasher.contents[joint_rgunabove]{armature} 282B ++A mechs/dasher/dasher.contents[joint_righttorsofront]{sites} 44B ++A mechs/dasher/dasher.contents[joint_root]{armature} 842B ++A mechs/dasher/dasher.contents[joint_ruarm]{armature} 282B ++A mechs/dasher/dasher.contents[joint_ruleg]{armature} 282B ++A mechs/dasher/dasher.contents[joint_torso]{armature} 282B ++A mechs/dasher/dasher.contents[joint_torsoabove]{armature} 2802B ++A mechs/dasher/dasher.contents[joint_torsoabove]{sites} 393B ++A mechs/dasher/dasher.contents[joint_torsobelow]{armature} 282B ++A mechs/dasher/dasher.contents[joint_vel]{armature} 282B ++A mechs/dasher/dasher.contents[joint_world]{armature} 282B ++A mechs/dasher/dasher.contents[joint_world]{sites} 40B ++A mechs/dasher/dasher.damage 1356B ++A mechs/dasher/dasher.data 12B ++A mechs/dasher/dasher.data[shadow] 117B ++A mechs/dasher/dasher.data{element} 100B ++A mechs/dasher/dasher.data{footsteps} 96B ++A mechs/dasher/dasher.data{gamemodel} 1636B ++A mechs/dasher/dasher.data{hierarchicalobb} 4928B ++A mechs/dasher/dasher.data{solidobb} 78B ++A mechs/dasher/dasher.engine 12B ++A mechs/dasher/dasher.engine{element} 100B ++A mechs/dasher/dasher.engine{gamemodel} 64B ++A mechs/dasher/dasher.instance 344B ++A mechs/dasher/dasher.subsystems 2238B ++A mechs/dasher/dasher.torso 12B ++A mechs/dasher/dasher.torso{element} 100B ++A mechs/dasher/dasher.torso{gamemodel} 1608B ++A mechs/dasher_destroyed/dasher_destroyed.data 12B ++A mechs/dasher_destroyed/dasher_destroyed.data{element} 100B ++A mechs/dasher_destroyed/dasher_destroyed.data{gamemodel} 32B ++A mechs/dasher_destroyed/dasher_destroyed.video 285B ++A mechs/dasher_destroyed/dasher_destroyed_solid.obb 78B ++A mechs/griffin/armaturedata/grf_hip.data 12B ++A mechs/griffin/armaturedata/grf_hip.data{element} 100B ++A mechs/griffin/armaturedata/grf_hip.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_lbtoe.data 12B ++A mechs/griffin/armaturedata/grf_lbtoe.data{element} 100B ++A mechs/griffin/armaturedata/grf_lbtoe.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_ldleg.data 12B ++A mechs/griffin/armaturedata/grf_ldleg.data{element} 100B ++A mechs/griffin/armaturedata/grf_ldleg.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_lfoot.data 12B ++A mechs/griffin/armaturedata/grf_lfoot.data{element} 100B ++A mechs/griffin/armaturedata/grf_lfoot.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_lftoe.data 12B ++A mechs/griffin/armaturedata/grf_lftoe.data{element} 100B ++A mechs/griffin/armaturedata/grf_lftoe.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_lgun.data 12B ++A mechs/griffin/armaturedata/grf_lgun.data{element} 100B ++A mechs/griffin/armaturedata/grf_lgun.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_luarm.data 12B ++A mechs/griffin/armaturedata/grf_luarm.data{element} 100B ++A mechs/griffin/armaturedata/grf_luarm.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_luleg.data 12B ++A mechs/griffin/armaturedata/grf_luleg.data{element} 100B ++A mechs/griffin/armaturedata/grf_luleg.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_rbtoe.data 12B ++A mechs/griffin/armaturedata/grf_rbtoe.data{element} 100B ++A mechs/griffin/armaturedata/grf_rbtoe.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_rdleg.data 12B ++A mechs/griffin/armaturedata/grf_rdleg.data{element} 100B ++A mechs/griffin/armaturedata/grf_rdleg.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_rfoot.data 12B ++A mechs/griffin/armaturedata/grf_rfoot.data{element} 100B ++A mechs/griffin/armaturedata/grf_rfoot.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_rftoe.data 12B ++A mechs/griffin/armaturedata/grf_rftoe.data{element} 100B ++A mechs/griffin/armaturedata/grf_rftoe.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_rgun.data 12B ++A mechs/griffin/armaturedata/grf_rgun.data{element} 100B ++A mechs/griffin/armaturedata/grf_rgun.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_ruarm.data 12B ++A mechs/griffin/armaturedata/grf_ruarm.data{element} 100B ++A mechs/griffin/armaturedata/grf_ruarm.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_ruleg.data 12B ++A mechs/griffin/armaturedata/grf_ruleg.data{element} 100B ++A mechs/griffin/armaturedata/grf_ruleg.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_specialone.data 12B ++A mechs/griffin/armaturedata/grf_specialone.data{element} 100B ++A mechs/griffin/armaturedata/grf_specialone.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_specialtwo.data 12B ++A mechs/griffin/armaturedata/grf_specialtwo.data{element} 100B ++A mechs/griffin/armaturedata/grf_specialtwo.data{gamemodel} 80B ++A mechs/griffin/armaturedata/grf_torso.data 12B ++A mechs/griffin/armaturedata/grf_torso.data{element} 100B ++A mechs/griffin/armaturedata/grf_torso.data{gamemodel} 80B ++A mechs/griffin/armaturedata/joint_cage.data 12B ++A mechs/griffin/armaturedata/joint_cage.data{element} 100B ++A mechs/griffin/armaturedata/joint_cage.data{gamemodel} 80B ++A mechs/griffin/armaturevideo/grf_hip.video 406B ++A mechs/griffin/armaturevideo/grf_lbtoe.video 406B ++A mechs/griffin/armaturevideo/grf_ldleg.video 406B ++A mechs/griffin/armaturevideo/grf_lfoot.video 406B ++A mechs/griffin/armaturevideo/grf_lftoe.video 406B ++A mechs/griffin/armaturevideo/grf_lgun.video 285B ++A mechs/griffin/armaturevideo/grf_luarm.video 406B ++A mechs/griffin/armaturevideo/grf_luleg.video 406B ++A mechs/griffin/armaturevideo/grf_rbtoe.video 406B ++A mechs/griffin/armaturevideo/grf_rdleg.video 406B ++A mechs/griffin/armaturevideo/grf_rfoot.video 406B ++A mechs/griffin/armaturevideo/grf_rftoe.video 406B ++A mechs/griffin/armaturevideo/grf_rgun.video 285B ++A mechs/griffin/armaturevideo/grf_ruarm.video 406B ++A mechs/griffin/armaturevideo/grf_ruleg.video 406B ++A mechs/griffin/armaturevideo/grf_specialone.video 406B ++A mechs/griffin/armaturevideo/grf_specialtwo.video 406B ++A mechs/griffin/armaturevideo/grf_torso.video 406B ++A mechs/griffin/armaturevideo/joint_cage.video 646B ++A mechs/griffin/griffin.contents 282B ++A mechs/griffin/griffin.contents[joint_cage]{sites} 124B ++A mechs/griffin/griffin.contents[joint_centertorsofront]{sites} 88B ++A mechs/griffin/griffin.contents[joint_hip]{armature} 282B ++A mechs/griffin/griffin.contents[joint_hipabove]{armature} 282B ++A mechs/griffin/griffin.contents[joint_hipbelow]{armature} 282B ++A mechs/griffin/griffin.contents[joint_lankle]{armature} 282B ++A mechs/griffin/griffin.contents[joint_lbelowankle]{armature} 842B ++A mechs/griffin/griffin.contents[joint_lbelowankle]{sites} 39B ++A mechs/griffin/griffin.contents[joint_ldleg]{armature} 282B ++A mechs/griffin/griffin.contents[joint_ldleg]{sites} 43B ++A mechs/griffin/griffin.contents[joint_lefttorsofront]{sites} 46B ++A mechs/griffin/griffin.contents[joint_lgun]{sites} 129B ++A mechs/griffin/griffin.contents[joint_lgunabove]{armature} 282B ++A mechs/griffin/griffin.contents[joint_luarm]{armature} 282B ++A mechs/griffin/griffin.contents[joint_luleg]{armature} 282B ++A mechs/griffin/griffin.contents[joint_rankle]{armature} 282B ++A mechs/griffin/griffin.contents[joint_rbelowankle]{armature} 842B ++A mechs/griffin/griffin.contents[joint_rbelowankle]{sites} 39B ++A mechs/griffin/griffin.contents[joint_rdleg]{armature} 282B ++A mechs/griffin/griffin.contents[joint_rdleg]{sites} 43B ++A mechs/griffin/griffin.contents[joint_rgun]{armature} 282B ++A mechs/griffin/griffin.contents[joint_rgun]{sites} 86B ++A mechs/griffin/griffin.contents[joint_rgunabove]{armature} 282B ++A mechs/griffin/griffin.contents[joint_righttorsofront]{sites} 46B ++A mechs/griffin/griffin.contents[joint_root]{armature} 842B ++A mechs/griffin/griffin.contents[joint_ruarm]{armature} 282B ++A mechs/griffin/griffin.contents[joint_ruarm]{sites} 42B ++A mechs/griffin/griffin.contents[joint_ruleg]{armature} 282B ++A mechs/griffin/griffin.contents[joint_specialone]{sites} 44B ++A mechs/griffin/griffin.contents[joint_specialtwo]{sites} 86B ++A mechs/griffin/griffin.contents[joint_torso]{armature} 282B ++A mechs/griffin/griffin.contents[joint_torsoabove]{armature} 2802B ++A mechs/griffin/griffin.contents[joint_torsoabove]{sites} 477B ++A mechs/griffin/griffin.contents[joint_torsobelow]{armature} 282B ++A mechs/griffin/griffin.contents[joint_vel]{armature} 282B ++A mechs/griffin/griffin.contents[joint_world]{armature} 282B ++A mechs/griffin/griffin.contents[joint_world]{sites} 40B ++A mechs/griffin/griffin.damage 1522B ++A mechs/griffin/griffin.data 12B ++A mechs/griffin/griffin.data[shadow] 117B ++A mechs/griffin/griffin.data{element} 100B ++A mechs/griffin/griffin.data{footsteps} 99B ++A mechs/griffin/griffin.data{gamemodel} 1636B ++A mechs/griffin/griffin.data{hierarchicalobb} 6500B ++A mechs/griffin/griffin.data{solidobb} 78B ++A mechs/griffin/griffin.engine 12B ++A mechs/griffin/griffin.engine{element} 100B ++A mechs/griffin/griffin.engine{gamemodel} 64B ++A mechs/griffin/griffin.instance 344B ++A mechs/griffin/griffin.subsystems 2818B ++A mechs/griffin/griffin.torso 12B ++A mechs/griffin/griffin.torso{element} 100B ++A mechs/griffin/griffin.torso{gamemodel} 1608B ++A mechs/griffin_destroyed/griffin_destroyed.data 12B ++A mechs/griffin_destroyed/griffin_destroyed.data{element} 100B ++A mechs/griffin_destroyed/griffin_destroyed.data{gamemodel} 32B ++A mechs/griffin_destroyed/griffin_destroyed.video 285B ++A mechs/griffin_destroyed/griffin_destroyed_solid.obb 78B ++A mechs/jenner2c/armaturedata/jec_hip.data 12B ++A mechs/jenner2c/armaturedata/jec_hip.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_hip.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_lbtoe.data 12B ++A mechs/jenner2c/armaturedata/jec_lbtoe.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_lbtoe.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_ldleg.data 12B ++A mechs/jenner2c/armaturedata/jec_ldleg.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_ldleg.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_lfoot.data 12B ++A mechs/jenner2c/armaturedata/jec_lfoot.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_lfoot.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_lftoe.data 12B ++A mechs/jenner2c/armaturedata/jec_lftoe.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_lftoe.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_lgun.data 12B ++A mechs/jenner2c/armaturedata/jec_lgun.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_lgun.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_luleg.data 12B ++A mechs/jenner2c/armaturedata/jec_luleg.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_luleg.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_rbtoe.data 12B ++A mechs/jenner2c/armaturedata/jec_rbtoe.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_rbtoe.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_rdleg.data 12B ++A mechs/jenner2c/armaturedata/jec_rdleg.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_rdleg.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_rfoot.data 12B ++A mechs/jenner2c/armaturedata/jec_rfoot.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_rfoot.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_rftoe.data 12B ++A mechs/jenner2c/armaturedata/jec_rftoe.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_rftoe.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_rgun.data 12B ++A mechs/jenner2c/armaturedata/jec_rgun.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_rgun.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_ruleg.data 12B ++A mechs/jenner2c/armaturedata/jec_ruleg.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_ruleg.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/jec_torso.data 12B ++A mechs/jenner2c/armaturedata/jec_torso.data{element} 100B ++A mechs/jenner2c/armaturedata/jec_torso.data{gamemodel} 80B ++A mechs/jenner2c/armaturedata/joint_cage.data 12B ++A mechs/jenner2c/armaturedata/joint_cage.data{element} 100B ++A mechs/jenner2c/armaturedata/joint_cage.data{gamemodel} 80B ++A mechs/jenner2c/armaturevideo/jec_hip.video 406B ++A mechs/jenner2c/armaturevideo/jec_lbtoe.video 406B ++A mechs/jenner2c/armaturevideo/jec_ldleg.video 406B ++A mechs/jenner2c/armaturevideo/jec_lfoot.video 406B ++A mechs/jenner2c/armaturevideo/jec_lftoe.video 406B ++A mechs/jenner2c/armaturevideo/jec_lgun.video 406B ++A mechs/jenner2c/armaturevideo/jec_luleg.video 406B ++A mechs/jenner2c/armaturevideo/jec_rbtoe.video 406B ++A mechs/jenner2c/armaturevideo/jec_rdleg.video 406B ++A mechs/jenner2c/armaturevideo/jec_rfoot.video 406B ++A mechs/jenner2c/armaturevideo/jec_rftoe.video 406B ++A mechs/jenner2c/armaturevideo/jec_rgun.video 406B ++A mechs/jenner2c/armaturevideo/jec_ruleg.video 406B ++A mechs/jenner2c/armaturevideo/jec_torso.video 683B ++A mechs/jenner2c/armaturevideo/joint_cage.video 646B ++A mechs/jenner2c/jenner_2c.contents 282B ++A mechs/jenner2c/jenner_2c.contents[joint_cage]{sites} 124B ++A mechs/jenner2c/jenner_2c.contents[joint_head]{sites} 45B ++A mechs/jenner2c/jenner_2c.contents[joint_hip]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_hipabove]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_hipbelow]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_lankle]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_lbelowankle]{armature} 842B ++A mechs/jenner2c/jenner_2c.contents[joint_lbelowankle]{sites} 39B ++A mechs/jenner2c/jenner_2c.contents[joint_ldleg]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_lgun]{sites} 86B ++A mechs/jenner2c/jenner_2c.contents[joint_lgunabove]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_luarm]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_luleg]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_rankle]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_rbelowankle]{armature} 842B ++A mechs/jenner2c/jenner_2c.contents[joint_rbelowankle]{sites} 39B ++A mechs/jenner2c/jenner_2c.contents[joint_rdleg]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_rgun]{sites} 45B ++A mechs/jenner2c/jenner_2c.contents[joint_rgunabove]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_root]{armature} 842B ++A mechs/jenner2c/jenner_2c.contents[joint_ruarm]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_ruleg]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_torso]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_torsoabove]{armature} 2522B ++A mechs/jenner2c/jenner_2c.contents[joint_torsoabove]{sites} 434B ++A mechs/jenner2c/jenner_2c.contents[joint_torsobelow]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_vel]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_world]{armature} 282B ++A mechs/jenner2c/jenner_2c.contents[joint_world]{sites} 40B ++A mechs/jenner2c/jenner_2c.damage 1356B ++A mechs/jenner2c/jenner_2c.data 12B ++A mechs/jenner2c/jenner_2c.data[shadow] 117B ++A mechs/jenner2c/jenner_2c.data{element} 100B ++A mechs/jenner2c/jenner_2c.data{footsteps} 102B ++A mechs/jenner2c/jenner_2c.data{gamemodel} 1636B ++A mechs/jenner2c/jenner_2c.data{hierarchicalobb} 4943B ++A mechs/jenner2c/jenner_2c.data{solidobb} 78B ++A mechs/jenner2c/jenner_2c.engine 12B ++A mechs/jenner2c/jenner_2c.engine{element} 100B ++A mechs/jenner2c/jenner_2c.engine{gamemodel} 64B ++A mechs/jenner2c/jenner_2c.instance 344B ++A mechs/jenner2c/jenner_2c.subsystems 2562B ++A mechs/jenner2c/jenner_2c.torso 12B ++A mechs/jenner2c/jenner_2c.torso{element} 100B ++A mechs/jenner2c/jenner_2c.torso{gamemodel} 1608B ++A mechs/jenner2c_destroyed/jenner2c_destroyed.data 12B ++A mechs/jenner2c_destroyed/jenner2c_destroyed.data{element} 100B ++A mechs/jenner2c_destroyed/jenner2c_destroyed.data{gamemodel} 32B ++A mechs/jenner2c_destroyed/jenner2c_destroyed.video 285B ++A mechs/jenner2c_destroyed/jenner2c_destroyed_solid.obb 78B ++A mechs/marauder/armaturedata/joint_cage.data 12B ++A mechs/marauder/armaturedata/joint_cage.data{element} 100B ++A mechs/marauder/armaturedata/joint_cage.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_hip.data 12B ++A mechs/marauder/armaturedata/mar_hip.data{element} 100B ++A mechs/marauder/armaturedata/mar_hip.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_lbtoe.data 12B ++A mechs/marauder/armaturedata/mar_lbtoe.data{element} 100B ++A mechs/marauder/armaturedata/mar_lbtoe.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_ldleg.data 12B ++A mechs/marauder/armaturedata/mar_ldleg.data{element} 100B ++A mechs/marauder/armaturedata/mar_ldleg.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_lfoot.data 12B ++A mechs/marauder/armaturedata/mar_lfoot.data{element} 100B ++A mechs/marauder/armaturedata/mar_lfoot.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_lgun.data 12B ++A mechs/marauder/armaturedata/mar_lgun.data{element} 100B ++A mechs/marauder/armaturedata/mar_lgun.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_litoe.data 12B ++A mechs/marauder/armaturedata/mar_litoe.data{element} 100B ++A mechs/marauder/armaturedata/mar_litoe.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_lotoe.data 12B ++A mechs/marauder/armaturedata/mar_lotoe.data{element} 100B ++A mechs/marauder/armaturedata/mar_lotoe.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_luarm.data 12B ++A mechs/marauder/armaturedata/mar_luarm.data{element} 100B ++A mechs/marauder/armaturedata/mar_luarm.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_luleg.data 12B ++A mechs/marauder/armaturedata/mar_luleg.data{element} 100B ++A mechs/marauder/armaturedata/mar_luleg.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_rbtoe.data 12B ++A mechs/marauder/armaturedata/mar_rbtoe.data{element} 100B ++A mechs/marauder/armaturedata/mar_rbtoe.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_rdleg.data 12B ++A mechs/marauder/armaturedata/mar_rdleg.data{element} 100B ++A mechs/marauder/armaturedata/mar_rdleg.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_rfoot.data 12B ++A mechs/marauder/armaturedata/mar_rfoot.data{element} 100B ++A mechs/marauder/armaturedata/mar_rfoot.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_rgun.data 12B ++A mechs/marauder/armaturedata/mar_rgun.data{element} 100B ++A mechs/marauder/armaturedata/mar_rgun.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_ritoe.data 12B ++A mechs/marauder/armaturedata/mar_ritoe.data{element} 100B ++A mechs/marauder/armaturedata/mar_ritoe.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_rotoe.data 12B ++A mechs/marauder/armaturedata/mar_rotoe.data{element} 100B ++A mechs/marauder/armaturedata/mar_rotoe.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_ruarm.data 12B ++A mechs/marauder/armaturedata/mar_ruarm.data{element} 100B ++A mechs/marauder/armaturedata/mar_ruarm.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_ruleg.data 12B ++A mechs/marauder/armaturedata/mar_ruleg.data{element} 100B ++A mechs/marauder/armaturedata/mar_ruleg.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_specialone.data 12B ++A mechs/marauder/armaturedata/mar_specialone.data{element} 100B ++A mechs/marauder/armaturedata/mar_specialone.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_specialtwo.data 12B ++A mechs/marauder/armaturedata/mar_specialtwo.data{element} 100B ++A mechs/marauder/armaturedata/mar_specialtwo.data{gamemodel} 80B ++A mechs/marauder/armaturedata/mar_torso.data 12B ++A mechs/marauder/armaturedata/mar_torso.data{element} 100B ++A mechs/marauder/armaturedata/mar_torso.data{gamemodel} 80B ++A mechs/marauder/armaturevideo/joint_cage.video 646B ++A mechs/marauder/armaturevideo/mar_hip.video 406B ++A mechs/marauder/armaturevideo/mar_lbtoe.video 285B ++A mechs/marauder/armaturevideo/mar_ldleg.video 406B ++A mechs/marauder/armaturevideo/mar_lfoot.video 406B ++A mechs/marauder/armaturevideo/mar_lgun.video 289B ++A mechs/marauder/armaturevideo/mar_litoe.video 406B ++A mechs/marauder/armaturevideo/mar_lotoe.video 285B ++A mechs/marauder/armaturevideo/mar_luarm.video 406B ++A mechs/marauder/armaturevideo/mar_luleg.video 406B ++A mechs/marauder/armaturevideo/mar_rbtoe.video 285B ++A mechs/marauder/armaturevideo/mar_rdleg.video 406B ++A mechs/marauder/armaturevideo/mar_rfoot.video 406B ++A mechs/marauder/armaturevideo/mar_rgun.video 285B ++A mechs/marauder/armaturevideo/mar_ritoe.video 406B ++A mechs/marauder/armaturevideo/mar_rotoe.video 406B ++A mechs/marauder/armaturevideo/mar_ruarm.video 406B ++A mechs/marauder/armaturevideo/mar_ruleg.video 406B ++A mechs/marauder/armaturevideo/mar_specialone.video 406B ++A mechs/marauder/armaturevideo/mar_specialtwo.video 406B ++A mechs/marauder/armaturevideo/mar_torso.video 683B ++A mechs/marauder/marauder.contents 282B ++A mechs/marauder/marauder.contents[joint_cage]{sites} 124B ++A mechs/marauder/marauder.contents[joint_hip]{armature} 282B ++A mechs/marauder/marauder.contents[joint_hipabove]{armature} 282B ++A mechs/marauder/marauder.contents[joint_hipbelow]{armature} 282B ++A mechs/marauder/marauder.contents[joint_lankle]{armature} 282B ++A mechs/marauder/marauder.contents[joint_lbelowankle]{armature} 1122B ++A mechs/marauder/marauder.contents[joint_lbelowankle]{sites} 39B ++A mechs/marauder/marauder.contents[joint_ldleg]{armature} 282B ++A mechs/marauder/marauder.contents[joint_lefttorsofront]{sites} 44B ++A mechs/marauder/marauder.contents[joint_lgun]{sites} 130B ++A mechs/marauder/marauder.contents[joint_lgunabove]{armature} 282B ++A mechs/marauder/marauder.contents[joint_luarm]{armature} 282B ++A mechs/marauder/marauder.contents[joint_luleg]{armature} 282B ++A mechs/marauder/marauder.contents[joint_rankle]{armature} 282B ++A mechs/marauder/marauder.contents[joint_rbelowankle]{armature} 1122B ++A mechs/marauder/marauder.contents[joint_rbelowankle]{sites} 39B ++A mechs/marauder/marauder.contents[joint_rdleg]{armature} 282B ++A mechs/marauder/marauder.contents[joint_rgun]{sites} 130B ++A mechs/marauder/marauder.contents[joint_rgunabove]{armature} 282B ++A mechs/marauder/marauder.contents[joint_righttorsofront]{sites} 44B ++A mechs/marauder/marauder.contents[joint_root]{armature} 842B ++A mechs/marauder/marauder.contents[joint_ruarm]{armature} 282B ++A mechs/marauder/marauder.contents[joint_ruleg]{armature} 282B ++A mechs/marauder/marauder.contents[joint_specialone]{sites} 42B ++A mechs/marauder/marauder.contents[joint_specialtwo]{sites} 42B ++A mechs/marauder/marauder.contents[joint_torso]{armature} 282B ++A mechs/marauder/marauder.contents[joint_torsoabove]{armature} 3082B ++A mechs/marauder/marauder.contents[joint_torsoabove]{sites} 477B ++A mechs/marauder/marauder.contents[joint_torsobelow]{armature} 282B ++A mechs/marauder/marauder.contents[joint_vel]{armature} 282B ++A mechs/marauder/marauder.contents[joint_world]{armature} 282B ++A mechs/marauder/marauder.contents[joint_world]{sites} 40B ++A mechs/marauder/marauder.damage 1688B ++A mechs/marauder/marauder.data 12B ++A mechs/marauder/marauder.data[shadow] 117B ++A mechs/marauder/marauder.data{element} 100B ++A mechs/marauder/marauder.data{footsteps} 102B ++A mechs/marauder/marauder.data{gamemodel} 1636B ++A mechs/marauder/marauder.data{hierarchicalobb} 7322B ++A mechs/marauder/marauder.data{solidobb} 78B ++A mechs/marauder/marauder.engine 12B ++A mechs/marauder/marauder.engine{element} 100B ++A mechs/marauder/marauder.engine{gamemodel} 64B ++A mechs/marauder/marauder.instance 344B ++A mechs/marauder/marauder.subsystems 4002B ++A mechs/marauder/marauder.torso 12B ++A mechs/marauder/marauder.torso{element} 100B ++A mechs/marauder/marauder.torso{gamemodel} 1608B ++A mechs/marauder_destroyed/marauder_destroyed.data 12B ++A mechs/marauder_destroyed/marauder_destroyed.data{element} 100B ++A mechs/marauder_destroyed/marauder_destroyed.data{gamemodel} 32B ++A mechs/marauder_destroyed/marauder_destroyed.video 285B ++A mechs/marauder_destroyed/marauder_destroyed_solid.obb 78B ++A mechs/thunderbolt/armaturedata/joint_cage.data 12B ++A mechs/thunderbolt/armaturedata/joint_cage.data{element} 100B ++A mechs/thunderbolt/armaturedata/joint_cage.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_hip.data 12B ++A mechs/thunderbolt/armaturedata/thu_hip.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_hip.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_lbtoe.data 12B ++A mechs/thunderbolt/armaturedata/thu_lbtoe.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_lbtoe.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_ldleg.data 12B ++A mechs/thunderbolt/armaturedata/thu_ldleg.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_ldleg.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_lfoot.data 12B ++A mechs/thunderbolt/armaturedata/thu_lfoot.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_lfoot.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_lftoe.data 12B ++A mechs/thunderbolt/armaturedata/thu_lftoe.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_lftoe.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_lgun.data 12B ++A mechs/thunderbolt/armaturedata/thu_lgun.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_lgun.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_luarm.data 12B ++A mechs/thunderbolt/armaturedata/thu_luarm.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_luarm.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_luleg.data 12B ++A mechs/thunderbolt/armaturedata/thu_luleg.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_luleg.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_rbtoe.data 12B ++A mechs/thunderbolt/armaturedata/thu_rbtoe.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_rbtoe.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_rdleg.data 12B ++A mechs/thunderbolt/armaturedata/thu_rdleg.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_rdleg.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_rfoot.data 12B ++A mechs/thunderbolt/armaturedata/thu_rfoot.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_rfoot.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_rftoe.data 12B ++A mechs/thunderbolt/armaturedata/thu_rftoe.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_rftoe.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_rgun.data 12B ++A mechs/thunderbolt/armaturedata/thu_rgun.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_rgun.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_ruarm.data 12B ++A mechs/thunderbolt/armaturedata/thu_ruarm.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_ruarm.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_ruleg.data 12B ++A mechs/thunderbolt/armaturedata/thu_ruleg.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_ruleg.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_specialone.data 12B ++A mechs/thunderbolt/armaturedata/thu_specialone.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_specialone.data{gamemodel} 80B ++A mechs/thunderbolt/armaturedata/thu_torso.data 12B ++A mechs/thunderbolt/armaturedata/thu_torso.data{element} 100B ++A mechs/thunderbolt/armaturedata/thu_torso.data{gamemodel} 80B ++A mechs/thunderbolt/armaturevideo/joint_cage.video 646B ++A mechs/thunderbolt/armaturevideo/thu_hip.video 406B ++A mechs/thunderbolt/armaturevideo/thu_lbtoe.video 285B ++A mechs/thunderbolt/armaturevideo/thu_ldleg.video 406B ++A mechs/thunderbolt/armaturevideo/thu_lfoot.video 406B ++A mechs/thunderbolt/armaturevideo/thu_lftoe.video 406B ++A mechs/thunderbolt/armaturevideo/thu_lgun.video 285B ++A mechs/thunderbolt/armaturevideo/thu_luarm.video 406B ++A mechs/thunderbolt/armaturevideo/thu_luleg.video 406B ++A mechs/thunderbolt/armaturevideo/thu_rbtoe.video 406B ++A mechs/thunderbolt/armaturevideo/thu_rdleg.video 406B ++A mechs/thunderbolt/armaturevideo/thu_rfoot.video 406B ++A mechs/thunderbolt/armaturevideo/thu_rftoe.video 406B ++A mechs/thunderbolt/armaturevideo/thu_rgun.video 285B ++A mechs/thunderbolt/armaturevideo/thu_ruarm.video 406B ++A mechs/thunderbolt/armaturevideo/thu_ruleg.video 406B ++A mechs/thunderbolt/armaturevideo/thu_specialone.video 285B ++A mechs/thunderbolt/armaturevideo/thu_torso.video 683B ++A mechs/thunderbolt/thunderbolt.contents 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_cage]{sites} 124B ++A mechs/thunderbolt/thunderbolt.contents[joint_hip]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_hipabove]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_hipbelow]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_lankle]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_lbelowankle]{armature} 842B ++A mechs/thunderbolt/thunderbolt.contents[joint_lbelowankle]{sites} 39B ++A mechs/thunderbolt/thunderbolt.contents[joint_ldleg]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_lefttorsofront]{sites} 44B ++A mechs/thunderbolt/thunderbolt.contents[joint_lgun]{sites} 130B ++A mechs/thunderbolt/thunderbolt.contents[joint_lgunabove]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_luarm]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_luleg]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_rankle]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_rbelowankle]{armature} 842B ++A mechs/thunderbolt/thunderbolt.contents[joint_rbelowankle]{sites} 39B ++A mechs/thunderbolt/thunderbolt.contents[joint_rdleg]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_rgun]{sites} 130B ++A mechs/thunderbolt/thunderbolt.contents[joint_rgunabove]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_righttorsofront]{sites} 44B ++A mechs/thunderbolt/thunderbolt.contents[joint_root]{armature} 842B ++A mechs/thunderbolt/thunderbolt.contents[joint_ruarm]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_ruleg]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_specialone]{sites} 86B ++A mechs/thunderbolt/thunderbolt.contents[joint_torso]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_torsoabove]{armature} 2802B ++A mechs/thunderbolt/thunderbolt.contents[joint_torsoabove]{sites} 393B ++A mechs/thunderbolt/thunderbolt.contents[joint_torsobelow]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_vel]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_world]{armature} 282B ++A mechs/thunderbolt/thunderbolt.contents[joint_world]{sites} 40B ++A mechs/thunderbolt/thunderbolt.damage 1522B ++A mechs/thunderbolt/thunderbolt.data 12B ++A mechs/thunderbolt/thunderbolt.data[shadow] 117B ++A mechs/thunderbolt/thunderbolt.data{element} 100B ++A mechs/thunderbolt/thunderbolt.data{footsteps} 110B ++A mechs/thunderbolt/thunderbolt.data{gamemodel} 1636B ++A mechs/thunderbolt/thunderbolt.data{hierarchicalobb} 5800B ++A mechs/thunderbolt/thunderbolt.data{solidobb} 78B ++A mechs/thunderbolt/thunderbolt.engine 12B ++A mechs/thunderbolt/thunderbolt.engine{element} 100B ++A mechs/thunderbolt/thunderbolt.engine{gamemodel} 64B ++A mechs/thunderbolt/thunderbolt.instance 344B ++A mechs/thunderbolt/thunderbolt.subsystems 5598B ++A mechs/thunderbolt/thunderbolt.torso 12B ++A mechs/thunderbolt/thunderbolt.torso{element} 100B ++A mechs/thunderbolt/thunderbolt.torso{gamemodel} 1608B ++A mechs/thunderbolt_destroyed/thunderbolt_destroyed.data 12B ++A mechs/thunderbolt_destroyed/thunderbolt_destroyed.data{element} 100B ++A mechs/thunderbolt_destroyed/thunderbolt_destroyed.data{gamemodel} 32B ++A mechs/thunderbolt_destroyed/thunderbolt_destroyed.obb 78B ++A mechs/thunderbolt_destroyed/thunderbolt_destroyed.video 285B +~ ablscripts/mwconst.abi A=26905B B=26643B +~ effects/ac_hit/ac5hit_lava.data{gamemodel} A=120B B=120B +~ effects/ac_hit/ac5hit_rock.data{gamemodel} A=120B B=120B +~ effects/gauss_flare/heavy_gauss_flare.audio A=128B B=128B +~ effects/longtom_hit/longtomhit_concrete.data{gamemodel} A=120B B=120B +~ effects/longtom_hit/longtomhit_darkbrowndirt.data{gamemodel} A=120B B=120B +~ effects/longtom_hit/longtomhit_darkconcrete.data{gamemodel} A=120B B=120B +~ effects/longtom_hit/longtomhit_darkgreydirt.data{gamemodel} A=120B B=120B +~ effects/searchlight/searchlightcone.data{gamemodel} A=120B B=120B +~ mechs/annihilator/annihilator.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/annihilator/annihilator.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/annihilator/annihilator.data{gamemodel} A=1636B B=1636B +~ mechs/annihilator/annihilator.subsystems A=6462B B=6462B +~ mechs/annihilator/annihilator.torso{gamemodel} A=1608B B=1608B +~ mechs/archer/archer.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/archer/archer.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/archer/archer.data{gamemodel} A=1636B B=1636B +~ mechs/archer/archer.subsystems A=6514B B=6514B +~ mechs/archer/archer.torso{gamemodel} A=1608B B=1608B +~ mechs/arcticwolf/arcticwolf.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/arcticwolf/arcticwolf.data{gamemodel} A=1636B B=1636B +~ mechs/arcticwolf/arcticwolf.subsystems A=4078B B=4078B +~ mechs/arcticwolf/arcticwolf.torso{gamemodel} A=1608B B=1608B +~ mechs/ares/ares.contents[joint_lbelowankle]{armature} A=1682B B=1682B +~ mechs/ares/ares.contents[joint_rbelowankle]{armature} A=1682B B=1682B +~ mechs/ares/ares.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/ares/ares.data{gamemodel} A=1636B B=1636B +~ mechs/ares/ares.subsystems A=5866B B=5866B +~ mechs/ares/ares.torso{gamemodel} A=1608B B=1608B +~ mechs/ares/armaturedata/ars_rgun.data{gamemodel} A=80B B=80B +~ mechs/argus/argus.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/argus/argus.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/argus/argus.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/argus/argus.data{gamemodel} A=1636B B=1636B +~ mechs/argus/argus.subsystems A=5650B B=5650B +~ mechs/argus/argus.torso{gamemodel} A=1608B B=1608B +~ mechs/assassin2/armaturedata/ass_hip.data{gamemodel} A=80B B=80B +~ mechs/assassin2/armaturedata/ass_rgun.data{gamemodel} A=80B B=80B +~ mechs/assassin2/armaturedata/ass_ruarm.data{gamemodel} A=80B B=80B +~ mechs/assassin2/assassin2.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/assassin2/assassin2.data{gamemodel} A=1636B B=1636B +~ mechs/assassin2/assassin2.subsystems A=4134B B=4134B +~ mechs/assassin2/assassin2.torso{gamemodel} A=1608B B=1608B +~ mechs/atlas/armaturedata/atl_hip.data{gamemodel} A=80B B=80B +~ mechs/atlas/armaturedata/atl_luleg.data{gamemodel} A=80B B=80B +~ mechs/atlas/armaturedata/atl_specialtwo.data{gamemodel} A=80B B=80B +~ mechs/atlas/atlas.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/atlas/atlas.data{gamemodel} A=1636B B=1636B +~ mechs/atlas/atlas.subsystems A=6082B B=6082B +~ mechs/atlas/atlas.torso{gamemodel} A=1608B B=1608B +~ mechs/avatar/armaturedata/ava_hip.data{gamemodel} A=80B B=80B +~ mechs/avatar/armaturedata/ava_torso.data{gamemodel} A=80B B=80B +~ mechs/avatar/avatar.contents[joint_lbelowankle]{armature} A=1402B B=1402B +~ mechs/avatar/avatar.contents[joint_rbelowankle]{armature} A=1402B B=1402B +~ mechs/avatar/avatar.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/avatar/avatar.data{gamemodel} A=1636B B=1636B +~ mechs/avatar/avatar.subsystems A=5922B B=5922B +~ mechs/awesome/armaturedata/awe_rgun.data{gamemodel} A=80B B=80B +~ mechs/awesome/awesome.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/awesome/awesome.data{gamemodel} A=1636B B=1636B +~ mechs/awesome/awesome.subsystems A=6242B B=6242B +~ mechs/awesome/awesome.torso{gamemodel} A=1608B B=1608B +~ mechs/awesome_destroyed/awesome_destroyed.data{gamemodel} A=32B B=32B +~ mechs/battlemaster/battlemaster.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/battlemaster/battlemaster.data{gamemodel} A=1636B B=1636B +~ mechs/battlemaster/battlemaster.subsystems A=3906B B=3906B +~ mechs/battlemaster/battlemaster.torso{gamemodel} A=1608B B=1608B +~ mechs/battlemaster2c/battlemaster2c.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/battlemaster2c/battlemaster2c.data{gamemodel} A=1636B B=1636B +~ mechs/battlemaster2c/battlemaster2c.subsystems A=3906B B=3906B +~ mechs/battlemaster2c/battlemaster2c.torso{gamemodel} A=1608B B=1608B +~ mechs/behemoth/behemoth.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/behemoth/behemoth.data{gamemodel} A=1636B B=1636B +~ mechs/behemoth/behemoth.subsystems A=5862B B=5862B +~ mechs/behemoth/behemoth.torso{gamemodel} A=1608B B=1608B +~ mechs/behemoth2/behemoth2.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/behemoth2/behemoth2.data{gamemodel} A=1636B B=1636B +~ mechs/behemoth2/behemoth2.subsystems A=5862B B=5862B +~ mechs/behemoth2/behemoth2.torso{gamemodel} A=1608B B=1608B +~ mechs/blackhawk/nova.contents[joint_lbelowankle]{armature} A=1682B B=1682B +~ mechs/blackhawk/nova.contents[joint_rbelowankle]{armature} A=1682B B=1682B +~ mechs/blackhawk/nova.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/blackhawk/nova.data{gamemodel} A=1636B B=1636B +~ mechs/blackhawk/nova.subsystems A=3642B B=3642B +~ mechs/blackhawk/nova.torso{gamemodel} A=1608B B=1608B +~ mechs/blacklanner/blacklanner.contents[joint_lbelowankle]{armature} A=1402B B=1402B +~ mechs/blacklanner/blacklanner.contents[joint_rbelowankle]{armature} A=1402B B=1402B +~ mechs/blacklanner/blacklanner.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/blacklanner/blacklanner.data{gamemodel} A=1636B B=1636B +~ mechs/blacklanner/blacklanner.subsystems A=3914B B=3914B +~ mechs/blacklanner/blacklanner.torso{gamemodel} A=1608B B=1608B +~ mechs/blacknight/blacknight.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/blacknight/blacknight.data{gamemodel} A=1636B B=1636B +~ mechs/blacknight/blacknight.subsystems A=6242B B=6242B +~ mechs/blacknight/blacknight.torso{gamemodel} A=1608B B=1608B +~ mechs/brigand/brigand.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/brigand/brigand.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/brigand/brigand.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/brigand/brigand.data{gamemodel} A=1636B B=1636B +~ mechs/brigand/brigand.subsystems A=3426B B=3426B +~ mechs/brigand/brigand.torso{gamemodel} A=1608B B=1608B +~ mechs/bushwacker/bushwacker.contents[joint_lbelowankle]{armature} A=1402B B=1402B +~ mechs/bushwacker/bushwacker.contents[joint_rbelowankle]{armature} A=1402B B=1402B +~ mechs/bushwacker/bushwacker.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/bushwacker/bushwacker.data{gamemodel} A=1636B B=1636B +~ mechs/bushwacker/bushwacker.subsystems A=5054B B=5054B +~ mechs/bushwacker/bushwacker.torso{gamemodel} A=1608B B=1608B +~ mechs/catapult/catapult.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/catapult/catapult.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/catapult/catapult.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/catapult/catapult.data{gamemodel} A=1636B B=1636B +~ mechs/catapult/catapult.subsystems A=3642B B=3642B +~ mechs/catapult/catapult.torso{gamemodel} A=1608B B=1608B +~ mechs/cauldronborn/cauldronborn.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/cauldronborn/cauldronborn.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/cauldronborn/cauldronborn.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/cauldronborn/cauldronborn.data{gamemodel} A=1636B B=1636B +~ mechs/cauldronborn/cauldronborn.subsystems A=4510B B=4510B +~ mechs/cauldronborn/cauldronborn.torso{gamemodel} A=1608B B=1608B +~ mechs/chimera/chimera.contents[joint_lbelowankle]{armature} A=1402B B=1402B +~ mechs/chimera/chimera.contents[joint_rbelowankle]{armature} A=1402B B=1402B +~ mechs/chimera/chimera.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/chimera/chimera.data{gamemodel} A=1636B B=1636B +~ mechs/chimera/chimera.subsystems A=5214B B=5214B +~ mechs/chimera/chimera.torso{gamemodel} A=1608B B=1608B +~ mechs/commando/commando.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/commando/commando.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/commando/commando.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/commando/commando.data{gamemodel} A=1636B B=1636B +~ mechs/commando/commando.subsystems A=2830B B=2830B +~ mechs/commando/commando.torso{gamemodel} A=1608B B=1608B +~ mechs/cougar/cougar.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/cougar/cougar.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/cougar/cougar.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/cougar/cougar.data{gamemodel} A=1636B B=1636B +~ mechs/cougar/cougar.subsystems A=4022B B=4022B +~ mechs/cougar/cougar.torso{gamemodel} A=1608B B=1608B +~ mechs/cyclops/cyclops.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/cyclops/cyclops.data{gamemodel} A=1636B B=1636B +~ mechs/cyclops/cyclops.subsystems A=5702B B=5702B +~ mechs/cyclops/cyclops.torso{gamemodel} A=1608B B=1608B +~ mechs/daishi/daishi.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/daishi/daishi.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/daishi/daishi.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/daishi/daishi.data{gamemodel} A=1636B B=1636B +~ mechs/daishi/daishi.subsystems A=5486B B=5486B +~ mechs/daishi/daishi.torso{gamemodel} A=1608B B=1608B +~ mechs/deimos/deimos.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/deimos/deimos.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/deimos/deimos.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/deimos/deimos.data{gamemodel} A=1636B B=1636B +~ mechs/deimos/deimos.subsystems A=6574B B=6574B +~ mechs/deimos/deimos.torso{gamemodel} A=1608B B=1608B +~ mechs/dragon/dragon.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/dragon/dragon.data{gamemodel} A=1636B B=1636B +~ mechs/dragon/dragon.subsystems A=4130B B=4130B +~ mechs/dragon/dragon.torso{gamemodel} A=1608B B=1608B +~ mechs/fafnir/fafnir.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/fafnir/fafnir.data{gamemodel} A=1636B B=1636B +~ mechs/fafnir/fafnir.subsystems A=4242B B=4242B +~ mechs/fafnir/fafnir.torso{gamemodel} A=1608B B=1608B +~ mechs/flea/flea.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/flea/flea.data{gamemodel} A=1636B B=1636B +~ mechs/flea/flea.subsystems A=3322B B=3322B +~ mechs/flea/flea.torso{gamemodel} A=1608B B=1608B +~ mechs/gladiator/gladiator.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/gladiator/gladiator.data{gamemodel} A=1636B B=1636B +~ mechs/gladiator/gladiator.subsystems A=7546B B=7546B +~ mechs/gladiator/gladiator.torso{gamemodel} A=1608B B=1608B +~ mechs/grizzly/grizzly.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/grizzly/grizzly.data{gamemodel} A=1636B B=1636B +~ mechs/grizzly/grizzly.subsystems A=4402B B=4402B +~ mechs/grizzly/grizzly.torso{gamemodel} A=1608B B=1608B +~ mechs/hauptmann/hauptmann.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/hauptmann/hauptmann.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/hauptmann/hauptmann.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/hauptmann/hauptmann.data{gamemodel} A=1636B B=1636B +~ mechs/hauptmann/hauptmann.subsystems A=6134B B=6134B +~ mechs/hauptmann/hauptmann.torso{gamemodel} A=1608B B=1608B +~ mechs/hellhound/hellhound.contents[joint_lbelowankle]{armature} A=1402B B=1402B +~ mechs/hellhound/hellhound.contents[joint_rbelowankle]{armature} A=1402B B=1402B +~ mechs/hellhound/hellhound.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/hellhound/hellhound.data{gamemodel} A=1636B B=1636B +~ mechs/hellhound/hellhound.subsystems A=4510B B=4510B +~ mechs/hellhound/hellhound.torso{gamemodel} A=1608B B=1608B +~ mechs/hellspawn/hellspawn.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/hellspawn/hellspawn.data{gamemodel} A=1636B B=1636B +~ mechs/hellspawn/hellspawn.subsystems A=4402B B=4402B +~ mechs/hellspawn/hellspawn.torso{gamemodel} A=1608B B=1608B +~ mechs/highlander/highlander.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/highlander/highlander.data{gamemodel} A=1636B B=1636B +~ mechs/highlander/highlander.subsystems A=5542B B=5542B +~ mechs/hollander/hollander.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/hollander/hollander.data{gamemodel} A=1636B B=1636B +~ mechs/hollander/hollander.subsystems A=4402B B=4402B +~ mechs/hollander/hollander.torso{gamemodel} A=1608B B=1608B +~ mechs/hunchback/hunchback.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/hunchback/hunchback.data{gamemodel} A=1636B B=1636B +~ mechs/hunchback/hunchback.subsystems A=4294B B=4294B +~ mechs/hunchback/hunchback.torso{gamemodel} A=1608B B=1608B +~ mechs/kodiak/kodiak.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/kodiak/kodiak.data{gamemodel} A=1636B B=1636B +~ mechs/kodiak/kodiak.subsystems A=5754B B=5754B +~ mechs/kodiak/kodiak.torso{gamemodel} A=1608B B=1608B +~ mechs/loki/loki.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/loki/loki.data{gamemodel} A=1636B B=1636B +~ mechs/loki/loki.subsystems A=4998B B=4998B +~ mechs/loki/loki.torso{gamemodel} A=1608B B=1608B +~ mechs/longbow/armaturedata/lon_lfoot.data{gamemodel} A=80B B=80B +~ mechs/longbow/longbow.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/longbow/longbow.data{gamemodel} A=1636B B=1636B +~ mechs/longbow/longbow.subsystems A=4294B B=4294B +~ mechs/longbow/longbow.torso{gamemodel} A=1608B B=1608B +~ mechs/madcat/madcat.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/madcat/madcat.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/madcat/madcat.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/madcat/madcat.data{gamemodel} A=1636B B=1636B +~ mechs/madcat/madcat.subsystems A=5702B B=5702B +~ mechs/madcat/madcat.torso{gamemodel} A=1608B B=1608B +~ mechs/madcat_destroyed/madcat_destroyed.data{gamemodel} A=32B B=32B +~ mechs/madcat_mkii/madcat_mkii.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/madcat_mkii/madcat_mkii.data{gamemodel} A=1636B B=1636B +~ mechs/madcat_mkii/madcat_mkii.subsystems A=7330B B=7330B +~ mechs/masakari/masakari.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/masakari/masakari.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/masakari/masakari.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/masakari/masakari.data{gamemodel} A=1636B B=1636B +~ mechs/masakari/masakari.subsystems A=5106B B=5106B +~ mechs/mauler/mauler.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/mauler/mauler.data{gamemodel} A=1636B B=1636B +~ mechs/mauler/mauler.subsystems A=5814B B=5814B +~ mechs/mauler/mauler.torso{gamemodel} A=1608B B=1608B +~ mechs/novacat/novacat.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/novacat/novacat.data{gamemodel} A=1636B B=1636B +~ mechs/novacat/novacat.subsystems A=5538B B=5538B +~ mechs/novacat/novacat.torso{gamemodel} A=1608B B=1608B +~ mechs/osiris/osiris.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/osiris/osiris.data{gamemodel} A=1636B B=1636B +~ mechs/osiris/osiris.subsystems A=4134B B=4134B +~ mechs/osiris/osiris.torso{gamemodel} A=1608B B=1608B +~ mechs/owens/owens.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/owens/owens.data{gamemodel} A=1636B B=1636B +~ mechs/owens/owens.subsystems A=3374B B=3374B +~ mechs/owens/owens.torso{gamemodel} A=1608B B=1608B +~ mechs/puma/puma.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/puma/puma.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/puma/puma.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/puma/puma.data{gamemodel} A=1636B B=1636B +~ mechs/puma/puma.subsystems A=3534B B=3534B +~ mechs/puma/puma.torso{gamemodel} A=1608B B=1608B +~ mechs/raven/raven.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/raven/raven.data{gamemodel} A=1636B B=1636B +~ mechs/raven/raven.subsystems A=3810B B=3810B +~ mechs/raven/raven.torso{gamemodel} A=1608B B=1608B +~ mechs/rifleman/rifleman.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/rifleman/rifleman.data{gamemodel} A=1636B B=1636B +~ mechs/rifleman/rifleman.subsystems A=4942B B=4942B +~ mechs/rifleman/rifleman.torso{gamemodel} A=1608B B=1608B +~ mechs/ryoken/ryoken.contents[joint_lbelowankle]{armature} A=1402B B=1402B +~ mechs/ryoken/ryoken.contents[joint_rbelowankle]{armature} A=1402B B=1402B +~ mechs/ryoken/ryoken.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/ryoken/ryoken.data{gamemodel} A=1636B B=1636B +~ mechs/ryoken/ryoken.subsystems A=4402B B=4402B +~ mechs/ryoken/ryoken.torso{gamemodel} A=1608B B=1608B +~ mechs/shadowcat/shadowcat.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/shadowcat/shadowcat.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/shadowcat/shadowcat.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/shadowcat/shadowcat.data{gamemodel} A=1636B B=1636B +~ mechs/shadowcat/shadowcat.subsystems A=4022B B=4022B +~ mechs/shadowcat/shadowcat.torso{gamemodel} A=1608B B=1608B +~ mechs/solitaire/solitaire.contents[joint_lbelowankle]{armature} A=1402B B=1402B +~ mechs/solitaire/solitaire.contents[joint_rbelowankle]{armature} A=1402B B=1402B +~ mechs/solitaire/solitaire.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/solitaire/solitaire.data{gamemodel} A=1636B B=1636B +~ mechs/solitaire/solitaire.subsystems A=3426B B=3426B +~ mechs/solitaire/solitaire.torso{gamemodel} A=1608B B=1608B +~ mechs/sunder/sunder.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/sunder/sunder.data{gamemodel} A=1636B B=1636B +~ mechs/sunder/sunder.subsystems A=6570B B=6570B +~ mechs/sunder/sunder.torso{gamemodel} A=1608B B=1608B +~ mechs/templar/templar.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/templar/templar.data{gamemodel} A=1636B B=1636B +~ mechs/templar/templar.subsystems A=7926B B=7926B +~ mechs/templar/templar.torso{gamemodel} A=1608B B=1608B +~ mechs/thanatos/thanatos.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/thanatos/thanatos.data{gamemodel} A=1636B B=1636B +~ mechs/thanatos/thanatos.subsystems A=5754B B=5754B +~ mechs/thanatos/thanatos.torso{gamemodel} A=1608B B=1608B +~ mechs/thor/thor.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/thor/thor.data{gamemodel} A=1636B B=1636B +~ mechs/thor/thor.subsystems A=5754B B=5754B +~ mechs/thor/thor.torso{gamemodel} A=1608B B=1608B +~ mechs/uller/uller.contents[joint_lbelowankle]{armature} A=1122B B=1122B +~ mechs/uller/uller.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/uller/uller.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/uller/uller.data{gamemodel} A=1636B B=1636B +~ mechs/uller/uller.subsystems A=3430B B=3430B +~ mechs/uller/uller.torso{gamemodel} A=1608B B=1608B +~ mechs/urbanmech/urbanmech.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/urbanmech/urbanmech.data{gamemodel} A=1636B B=1636B +~ mechs/urbanmech/urbanmech.subsystems A=2450B B=2450B +~ mechs/urbanmech/urbanmech.torso{gamemodel} A=1608B B=1608B +~ mechs/uziel/uziel.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/uziel/uziel.data{gamemodel} A=1636B B=1636B +~ mechs/uziel/uziel.subsystems A=4674B B=4674B +~ mechs/uziel/uziel.torso{gamemodel} A=1608B B=1608B +~ mechs/victor/victor.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/victor/victor.data{gamemodel} A=1636B B=1636B +~ mechs/victor/victor.subsystems A=4782B B=4782B +~ mechs/victor/victor.torso{gamemodel} A=1608B B=1608B +~ mechs/vulture/vulture.contents[joint_rbelowankle]{armature} A=1122B B=1122B +~ mechs/vulture/vulture.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/vulture/vulture.data{gamemodel} A=1636B B=1636B +~ mechs/vulture/vulture.subsystems A=6030B B=6030B +~ mechs/vulture/vulture.torso{gamemodel} A=1608B B=1608B +~ mechs/warhammer/warhammer.contents[joint_torsoabove]{armature} A=3082B B=3082B +~ mechs/warhammer/warhammer.data{gamemodel} A=1636B B=1636B +~ mechs/warhammer/warhammer.instance A=344B B=344B +~ mechs/warhammer/warhammer.subsystems A=4674B B=4674B +~ mechs/warhammer/warhammer.torso{gamemodel} A=1608B B=1608B +~ mechs/wolfhound/wolfhound.contents[joint_torsoabove]{armature} A=2802B B=2802B +~ mechs/wolfhound/wolfhound.data{gamemodel} A=1636B B=1636B +~ mechs/wolfhound/wolfhound.subsystems A=4458B B=4458B +~ mechs/wolfhound/wolfhound.torso{gamemodel} A=1608B B=1608B +~ mechs/zeus/zeus.contents[joint_torsoabove]{armature} A=2522B B=2522B +~ mechs/zeus/zeus.data{gamemodel} A=1636B B=1636B +~ mechs/zeus/zeus.subsystems A=5862B B=5862B +~ mechs/zeus/zeus.torso{gamemodel} A=1608B B=1608B +~ tables/mechchassistable.tbl A=3459B B=3158B +~ tables/mechtable.tbl A=3743B B=3418B +~ weapons/artillerymark/artillerymark.data{gamemodel} A=160B B=160B +~ weapons/bomb/srmbomb.data{gamemodel} A=136B B=136B +~ weapons/longtom/shorttom.data{gamemodel} A=136B B=136B +~ weapons/lrm/lrm.data{gamemodel} A=136B B=136B +~ weapons/mrm/mrm.data{gamemodel} A=136B B=136B +~ weapons/mrm/smrm.data{gamemodel} A=136B B=136B +~ weapons/srm/srm.data{gamemodel} A=136B B=136B +~ weapons/srm/ssrm.data{gamemodel} A=136B B=136B +~ weaponsubsystems/acweaponsubsystem/ac10.data{gamemodel} A=176B B=176B +~ weaponsubsystems/acweaponsubsystem/ac2.data{gamemodel} A=176B B=176B +~ weaponsubsystems/acweaponsubsystem/ac20.data{gamemodel} A=176B B=176B +~ weaponsubsystems/acweaponsubsystem/ac5.data{gamemodel} A=176B B=176B +~ weaponsubsystems/artillerystrikeweaponsubsystem/artillerystrike.data{gamemodel} A=176B B=176B +~ weaponsubsystems/bombweaponsubsystem/srmbomb.data{gamemodel} A=180B B=180B +~ weaponsubsystems/flamerweaponsubsystem/clanflamer.data{gamemodel} A=176B B=176B +~ weaponsubsystems/flamerweaponsubsystem/flamer.data{gamemodel} A=176B B=176B +~ weaponsubsystems/flareweaponsubsystem/flare.data{gamemodel} A=176B B=176B +~ weaponsubsystems/gaussweaponsubsystem/clangaussrifle.data{gamemodel} A=176B B=176B +~ weaponsubsystems/gaussweaponsubsystem/gaussrifle.data{gamemodel} A=176B B=176B +~ weaponsubsystems/gaussweaponsubsystem/heavygaussrifle.data{gamemodel} A=176B B=176B +~ weaponsubsystems/gaussweaponsubsystem/lightgaussrifle.data{gamemodel} A=176B B=176B +~ weaponsubsystems/highexplosiveweaponsubsystem/highexplosive.data{gamemodel} A=176B B=176B +~ weaponsubsystems/laserweaponsubsystem/bombastlaser.data{gamemodel} A=168B B=168B +~ weaponsubsystems/laserweaponsubsystem/clanerlargelaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/laserweaponsubsystem/clanermediumlaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/laserweaponsubsystem/clanersmalllaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/laserweaponsubsystem/erlargelaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/laserweaponsubsystem/ermediumlaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/laserweaponsubsystem/largelaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/laserweaponsubsystem/mediumlaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/laserweaponsubsystem/smalllaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/lbxacweaponsubsystem/clanlbxac10.data{gamemodel} A=184B B=184B +~ weaponsubsystems/lbxacweaponsubsystem/clanlbxac20.data{gamemodel} A=184B B=184B +~ weaponsubsystems/lbxacweaponsubsystem/lbxac10.data{gamemodel} A=184B B=184B +~ weaponsubsystems/lbxacweaponsubsystem/lbxac20.data{gamemodel} A=184B B=184B +~ weaponsubsystems/longtomweaponsubsystem/longtom.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/clanlrm10.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/clanlrm15.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/clanlrm20.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/clanlrm5.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/lrm10.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/lrm15.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/lrm20.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/lrm5.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/sharedclanlrm.data{gamemodel} A=180B B=180B +~ weaponsubsystems/lrmweaponsubsystem/sharedlrm.data{gamemodel} A=180B B=180B +~ weaponsubsystems/machinegunweaponsubsystem/clanmachinegun.data{gamemodel} A=176B B=176B +~ weaponsubsystems/machinegunweaponsubsystem/machinegun.data{gamemodel} A=176B B=176B +~ weaponsubsystems/mrmweaponsubsystem/mrm10.data{gamemodel} A=180B B=180B +~ weaponsubsystems/mrmweaponsubsystem/mrm20.data{gamemodel} A=180B B=180B +~ weaponsubsystems/mrmweaponsubsystem/mrm30.data{gamemodel} A=180B B=180B +~ weaponsubsystems/mrmweaponsubsystem/mrm40.data{gamemodel} A=180B B=180B +~ weaponsubsystems/mrmweaponsubsystem/sharedmrm.data{gamemodel} A=180B B=180B +~ weaponsubsystems/narcbeaconweaponsubsystem/clannarcbeacon.data{gamemodel} A=176B B=176B +~ weaponsubsystems/narcbeaconweaponsubsystem/narcbeacon.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ppcweaponsubsystem/clanerppc.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ppcweaponsubsystem/erppc.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ppcweaponsubsystem/ppc.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ppcweaponsubsystem/zeroppc.data{gamemodel} A=176B B=176B +~ weaponsubsystems/pulselaserweaponsubsystem/clanlargepulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/pulselaserweaponsubsystem/clanmediumpulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/pulselaserweaponsubsystem/clansmallpulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/pulselaserweaponsubsystem/largepulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/pulselaserweaponsubsystem/largexpulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/pulselaserweaponsubsystem/mediumpulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/pulselaserweaponsubsystem/mediumxpulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/pulselaserweaponsubsystem/smallpulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/pulselaserweaponsubsystem/smallxpulselaser.data{gamemodel} A=124B B=124B +~ weaponsubsystems/shorttomweaponsubsystem/shorttom.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/clansmrm10.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/clansmrm20.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/clansmrm30.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/clansmrm40.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/sharedclansmrm.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/sharedsmrm.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/smrm10.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/smrm20.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/smrm30.data{gamemodel} A=180B B=180B +~ weaponsubsystems/smrmweaponsubsystem/smrm40.data{gamemodel} A=180B B=180B +~ weaponsubsystems/srmweaponsubsystem/clansrm2.data{gamemodel} A=180B B=180B +~ weaponsubsystems/srmweaponsubsystem/clansrm4.data{gamemodel} A=180B B=180B +~ weaponsubsystems/srmweaponsubsystem/clansrm6.data{gamemodel} A=180B B=180B +~ weaponsubsystems/srmweaponsubsystem/sharedsrm.data{gamemodel} A=180B B=180B +~ weaponsubsystems/srmweaponsubsystem/srm2.data{gamemodel} A=180B B=180B +~ weaponsubsystems/srmweaponsubsystem/srm4.data{gamemodel} A=180B B=180B +~ weaponsubsystems/srmweaponsubsystem/srm6.data{gamemodel} A=180B B=180B +~ weaponsubsystems/ssrmweaponsubsystem/clanssrm2.data{gamemodel} A=180B B=180B +~ weaponsubsystems/ssrmweaponsubsystem/clanssrm4.data{gamemodel} A=180B B=180B +~ weaponsubsystems/ssrmweaponsubsystem/clanssrm6.data{gamemodel} A=180B B=180B +~ weaponsubsystems/ssrmweaponsubsystem/hssrm.data{gamemodel} A=180B B=180B +~ weaponsubsystems/ssrmweaponsubsystem/sharedclanssrm.data{gamemodel} A=180B B=180B +~ weaponsubsystems/ssrmweaponsubsystem/ssrm2.data{gamemodel} A=180B B=180B +~ weaponsubsystems/ssrmweaponsubsystem/ssrm4.data{gamemodel} A=180B B=180B +~ weaponsubsystems/ssrmweaponsubsystem/ssrm6.data{gamemodel} A=180B B=180B +~ weaponsubsystems/thunderboltweaponsubsystem/thunderbolt.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ultraacweaponsubsystem/clanultraac10.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ultraacweaponsubsystem/clanultraac2.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ultraacweaponsubsystem/clanultraac20.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ultraacweaponsubsystem/clanultraac5.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ultraacweaponsubsystem/ultraac10.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ultraacweaponsubsystem/ultraac2.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ultraacweaponsubsystem/ultraac20.data{gamemodel} A=176B B=176B +~ weaponsubsystems/ultraacweaponsubsystem/ultraac5.data{gamemodel} A=176B B=176B diff --git a/MW4COMPARE/reports/hsh-diff.txt b/MW4COMPARE/reports/hsh-diff.txt new file mode 100644 index 00000000..86311db7 --- /dev/null +++ b/MW4COMPARE/reports/hsh-diff.txt @@ -0,0 +1,304 @@ +# V4H=279 files, OURS=456 files | identical=182 differ=68 V4H_only=29 OURS_only=206 ++V4H decals/DDC.bmp 30056 ++V4H hud/Champion.bmp 263224 ++V4H hud/Convert.txt 586 ++V4H hud/Dasher.bmp 263224 ++V4H hud/Griffin.bmp 263224 ++V4H hud/jenner2c.bmp 263224 ++V4H hud/Marauder.bmp 263224 ++V4H hud/Thunderbolt.bmp 263224 ++V4H Mechs/battlemasteriic.bmp 600054 ++V4H Mechs/champion.bmp 600054 ++V4H Mechs/Dasher.bmp 600054 ++V4H Mechs/griffin.bmp 600054 ++V4H Mechs/jenner2c.bmp 600054 ++V4H Mechs/mad cat mk.ii.bmp 600054 ++V4H Mechs/marauder.bmp 600054 ++V4H Mechs/thunderbolt.bmp 600054 ++V4H MFD/champion.bmp 43254 ++V4H MFD/Dasher.bmp 43254 ++V4H MFD/griffin.bmp 43254 ++V4H MFD/jenner2c.bmp 43254 ++V4H MFD/marauder.bmp 43254 ++V4H MFD/thunderbolt.bmp 43254 ++V4H radar/hud/champion.bmp 263224 ++V4H radar/hud/Convert.txt 579 ++V4H radar/hud/dasher.bmp 263224 ++V4H radar/hud/griffin.bmp 263224 ++V4H radar/hud/Jenner2c.bmp 263224 ++V4H radar/hud/marauder.bmp 263224 ++V4H radar/hud/thunderbolt.bmp 263224 +-OURS decals/decal_46.bmp 11080 +-OURS decals/decal_47.bmp 30056 +-OURS decals/decal_49.bmp 30056 +-OURS hud/annihilator.bmp 263224 +-OURS hud/archer.bmp 263224 +-OURS hud/arcticwolf.bmp 263224 +-OURS hud/ares.bmp 263224 +-OURS hud/argus.bmp 263224 +-OURS hud/assassin2.bmp 263224 +-OURS hud/assassinii.bmp 263224 +-OURS hud/Atlas.bmp 263224 +-OURS hud/avatar.bmp 263224 +-OURS hud/awesome.bmp 263224 +-OURS hud/battlemaster.bmp 263224 +-OURS hud/battlemasteriic.bmp 263224 +-OURS hud/behemoth.bmp 263224 +-OURS hud/behemothii.bmp 263224 +-OURS hud/blackhawk.bmp 263224 +-OURS hud/blackknight.bmp 263224 +-OURS hud/blacklanner.bmp 263224 +-OURS hud/brigand.bmp 263224 +-OURS hud/bushwacker.bmp 263224 +-OURS hud/catapult.bmp 263224 +-OURS hud/cauldronborn.bmp 263224 +-OURS hud/chimera.bmp 263224 +-OURS hud/commando.bmp 263224 +-OURS hud/cougar.bmp 263224 +-OURS hud/cyclops.bmp 263224 +-OURS hud/daishi.bmp 263224 +-OURS hud/deimos.bmp 263224 +-OURS hud/dragon.bmp 263224 +-OURS hud/fafnir.bmp 263224 +-OURS hud/flea.bmp 263224 +-OURS hud/gladiator.bmp 263224 +-OURS hud/grizzly.bmp 263224 +-OURS hud/hauptmann.bmp 263224 +-OURS hud/hellhound.bmp 263224 +-OURS hud/hellspawn.bmp 263224 +-OURS hud/highlander.bmp 263224 +-OURS hud/hollanderii.bmp 263224 +-OURS hud/hunchback.bmp 263224 +-OURS hud/kodiak.bmp 263224 +-OURS hud/loki.bmp 263224 +-OURS hud/longbow.bmp 263224 +-OURS hud/madcat.bmp 263224 +-OURS hud/madcat2.bmp 263224 +-OURS hud/masakari.bmp 263224 +-OURS hud/mauler.bmp 263224 +-OURS hud/novacat.bmp 263224 +-OURS hud/osiris.bmp 263224 +-OURS hud/owens.bmp 263224 +-OURS hud/puma.bmp 263224 +-OURS hud/raven.bmp 263224 +-OURS hud/ryoken.bmp 263224 +-OURS hud/shadowcat.bmp 263224 +-OURS hud/solitaire.bmp 263224 +-OURS hud/sunder.bmp 263224 +-OURS hud/templar.bmp 263224 +-OURS hud/thanatos.bmp 263224 +-OURS hud/thor.bmp 263224 +-OURS hud/uller.bmp 263224 +-OURS hud/urbanmech.bmp 263224 +-OURS hud/uziel.bmp 263224 +-OURS hud/victor.bmp 263224 +-OURS hud/vulture.bmp 263224 +-OURS hud/warhammer.bmp 263224 +-OURS hud/wolfhound.bmp 263224 +-OURS hud/zeus.bmp 263224 +-OURS Mechs/battlemaster iic.bmp 201068 +-OURS Mechs/behemoth ii.bmp 201080 +-OURS Mechs/behemoth.bmp 201080 +-OURS Mechs/black hawk.bmp 201080 +-OURS Mechs/longbow.bmp 201080 +-OURS Mechs/mad cat mkii.bmp 201080 +-OURS Mechs/solitare.bmp 201080 +-OURS Mechs/victor.bmp 201080 +-OURS MFD/annihilator.bmp 15480 +-OURS MFD/archer.bmp 43256 +-OURS MFD/arcticwolf.bmp 43256 +-OURS MFD/ares.bmp 15480 +-OURS MFD/argus.bmp 43256 +-OURS MFD/assassin2.bmp 15480 +-OURS MFD/atlas.bmp 43256 +-OURS MFD/avatar.bmp 15480 +-OURS MFD/awesome.bmp 43256 +-OURS MFD/battlemaster.bmp 15480 +-OURS MFD/battlemasteriic.bmp 15480 +-OURS MFD/behemoth.bmp 15480 +-OURS MFD/behemothii.bmp 15480 +-OURS MFD/blackhawk.bmp 15480 +-OURS MFD/blackknight.bmp 43256 +-OURS MFD/blacklanner.bmp 15320 +-OURS MFD/brigand.bmp 43256 +-OURS MFD/bushwacker.bmp 43256 +-OURS MFD/catapult.bmp 43256 +-OURS MFD/cauldronborn.bmp 43256 +-OURS MFD/chimera.bmp 43256 +-OURS MFD/commando.bmp 43256 +-OURS MFD/cougar.bmp 15480 +-OURS MFD/cyclops.bmp 43256 +-OURS MFD/daishi.bmp 15480 +-OURS MFD/deimos.bmp 15480 +-OURS MFD/dragon.bmp 15480 +-OURS MFD/fafnir.bmp 15480 +-OURS MFD/flea.bmp 15480 +-OURS MFD/gladiator.bmp 15480 +-OURS MFD/grizzly.bmp 43256 +-OURS MFD/hauptmann.bmp 43256 +-OURS MFD/hellhound.bmp 43256 +-OURS MFD/hellspawn.bmp 15480 +-OURS MFD/highlander.bmp 43256 +-OURS MFD/hollanderii.bmp 43256 +-OURS MFD/hunchback.bmp 43256 +-OURS MFD/kodiak.bmp 15480 +-OURS MFD/loki.bmp 15480 +-OURS MFD/longbow.bmp 15480 +-OURS MFD/madcat.bmp 15476 +-OURS MFD/madcat2.bmp 43256 +-OURS MFD/masakari.bmp 43256 +-OURS MFD/mauler.bmp 43256 +-OURS MFD/novacat.bmp 43256 +-OURS MFD/osiris.bmp 43256 +-OURS MFD/owens.bmp 43256 +-OURS MFD/puma.bmp 43256 +-OURS MFD/raven.bmp 43256 +-OURS MFD/ryoken.bmp 43256 +-OURS MFD/shadowcat.bmp 15480 +-OURS MFD/solitaire.bmp 15480 +-OURS MFD/sunder.bmp 15480 +-OURS MFD/templar.bmp 43256 +-OURS MFD/thanatos.bmp 15480 +-OURS MFD/thor.bmp 43256 +-OURS MFD/uller.bmp 43256 +-OURS MFD/urbanmech.bmp 43256 +-OURS MFD/uziel.bmp 43256 +-OURS MFD/victor.bmp 15480 +-OURS MFD/vulture.bmp 43256 +-OURS MFD/warhammer.bmp 15480 +-OURS MFD/wolfhound.bmp 43256 +-OURS MFD/zeus.bmp 43256 +-OURS radar/hud/annihilator.bmp 263224 +-OURS radar/hud/archer.bmp 263224 +-OURS radar/hud/arcticwolf.bmp 263224 +-OURS radar/hud/ares.bmp 263224 +-OURS radar/hud/argus.bmp 263224 +-OURS radar/hud/assassin2.bmp 263224 +-OURS radar/hud/assassinii.bmp 263224 +-OURS radar/hud/Atlas.bmp 263224 +-OURS radar/hud/avatar.bmp 263224 +-OURS radar/hud/awesome.bmp 263224 +-OURS radar/hud/battlemaster.bmp 263224 +-OURS radar/hud/battlemasteriic.bmp 263224 +-OURS radar/hud/behemoth.bmp 263224 +-OURS radar/hud/behemothii.bmp 263224 +-OURS radar/hud/blackhawk.bmp 263224 +-OURS radar/hud/blackknight.bmp 263224 +-OURS radar/hud/blacklanner.bmp 263224 +-OURS radar/hud/brigand.bmp 263224 +-OURS radar/hud/bushwacker.bmp 263224 +-OURS radar/hud/catapult.bmp 263224 +-OURS radar/hud/cauldronborn.bmp 263224 +-OURS radar/hud/chimera.bmp 263224 +-OURS radar/hud/commando.bmp 263224 +-OURS radar/hud/cougar.bmp 263224 +-OURS radar/hud/cyclops.bmp 263224 +-OURS radar/hud/daishi.bmp 263224 +-OURS radar/hud/deimos.bmp 263224 +-OURS radar/hud/dragon.bmp 263224 +-OURS radar/hud/Fafnir.bmp 263224 +-OURS radar/hud/flea.bmp 263224 +-OURS radar/hud/gladiator.bmp 263224 +-OURS radar/hud/grizzly.bmp 263224 +-OURS radar/hud/hauptmann.bmp 263224 +-OURS radar/hud/hellhound.bmp 263224 +-OURS radar/hud/hellspawn.bmp 263224 +-OURS radar/hud/highlander.bmp 263224 +-OURS radar/hud/hollanderii.bmp 263224 +-OURS radar/hud/hunchback.bmp 263224 +-OURS radar/hud/kodiak.bmp 263224 +-OURS radar/hud/loki.bmp 263224 +-OURS radar/hud/longbow.bmp 263224 +-OURS radar/hud/madcat.bmp 263224 +-OURS radar/hud/madcat2.bmp 263224 +-OURS radar/hud/masakari.bmp 263224 +-OURS radar/hud/mauler.bmp 263224 +-OURS radar/hud/novacat.bmp 263224 +-OURS radar/hud/osiris.bmp 263224 +-OURS radar/hud/owens.bmp 263224 +-OURS radar/hud/puma.bmp 263224 +-OURS radar/hud/raven.bmp 263224 +-OURS radar/hud/ryoken.bmp 263224 +-OURS radar/hud/shadowcat.bmp 263224 +-OURS radar/hud/solitaire.bmp 263224 +-OURS radar/hud/sunder.bmp 263224 +-OURS radar/hud/templar.bmp 263224 +-OURS radar/hud/thanatos.bmp 263224 +-OURS radar/hud/thor.bmp 263224 +-OURS radar/hud/Thumbs.db 8704 +-OURS radar/hud/uller.bmp 263224 +-OURS radar/hud/urbanmech.bmp 263224 +-OURS radar/hud/uziel.bmp 263224 +-OURS radar/hud/victor.bmp 263224 +-OURS radar/hud/vulture.bmp 263224 +-OURS radar/hud/warhammer.bmp 263224 +-OURS radar/hud/wolfhound.bmp 263224 +-OURS radar/hud/zeus.bmp 263224 +~DIF decals/Thumbs.db V4H=8704B OURS=8704B +~DIF hud/rifleman.bmp V4H=263224B OURS=263224B +~DIF hud/Thumbs.db V4H=8192B OURS=830768B +~DIF logo.bmp V4H=481080B OURS=481080B +~DIF Mechs/annihilator.bmp V4H=600056B OURS=201080B +~DIF Mechs/archer.bmp V4H=600054B OURS=201080B +~DIF Mechs/arctic wolf.bmp V4H=600054B OURS=201080B +~DIF Mechs/ares.bmp V4H=600054B OURS=201080B +~DIF Mechs/argus.bmp V4H=600054B OURS=201072B +~DIF Mechs/assassin ii.bmp V4H=600056B OURS=201080B +~DIF Mechs/atlas.bmp V4H=600054B OURS=201080B +~DIF Mechs/avatar.bmp V4H=600054B OURS=201080B +~DIF Mechs/awesome.bmp V4H=600054B OURS=201080B +~DIF Mechs/battlemaster.bmp V4H=600054B OURS=201072B +~DIF Mechs/black knight.bmp V4H=600056B OURS=201080B +~DIF Mechs/black lanner.bmp V4H=600056B OURS=201080B +~DIF Mechs/brigand.bmp V4H=600056B OURS=201080B +~DIF Mechs/bushwacker.bmp V4H=600054B OURS=201080B +~DIF Mechs/catapult.bmp V4H=600054B OURS=201080B +~DIF Mechs/cauldronborn.bmp V4H=600054B OURS=201080B +~DIF Mechs/chimera.bmp V4H=600054B OURS=201080B +~DIF Mechs/commando.bmp V4H=600056B OURS=201080B +~DIF Mechs/cougar.bmp V4H=600054B OURS=201080B +~DIF Mechs/cyclops.bmp V4H=600054B OURS=201080B +~DIF Mechs/daishi.bmp V4H=600056B OURS=201080B +~DIF Mechs/deimos.bmp V4H=600054B OURS=201080B +~DIF Mechs/dragon.bmp V4H=600054B OURS=201080B +~DIF Mechs/fafnir.bmp V4H=600054B OURS=201080B +~DIF Mechs/flea.bmp V4H=600054B OURS=201080B +~DIF Mechs/gladiator.bmp V4H=600054B OURS=201080B +~DIF Mechs/grizzly.bmp V4H=600056B OURS=201080B +~DIF Mechs/Hauptmann.bmp V4H=600054B OURS=201080B +~DIF Mechs/hellhound.bmp V4H=600054B OURS=201080B +~DIF Mechs/hellspawn.bmp V4H=600054B OURS=201080B +~DIF Mechs/highlander.bmp V4H=600054B OURS=201080B +~DIF Mechs/hollander ii.bmp V4H=600054B OURS=201080B +~DIF Mechs/hunchback.bmp V4H=600056B OURS=201080B +~DIF Mechs/kodiak.bmp V4H=600054B OURS=201072B +~DIF Mechs/loki.bmp V4H=600054B OURS=201080B +~DIF Mechs/mad cat.bmp V4H=600054B OURS=201080B +~DIF Mechs/Masakari.bmp V4H=600054B OURS=201080B +~DIF Mechs/mauler.bmp V4H=600054B OURS=201080B +~DIF Mechs/nova cat.bmp V4H=600054B OURS=201080B +~DIF Mechs/osiris.bmp V4H=600054B OURS=201080B +~DIF Mechs/owens.bmp V4H=600054B OURS=201080B +~DIF Mechs/puma.bmp V4H=600054B OURS=201080B +~DIF Mechs/raven.bmp V4H=600054B OURS=201080B +~DIF Mechs/rifleman.bmp V4H=600054B OURS=201080B +~DIF Mechs/Ryoken.bmp V4H=600054B OURS=201080B +~DIF Mechs/shadow cat.bmp V4H=600054B OURS=201080B +~DIF Mechs/sunder.bmp V4H=600054B OURS=201080B +~DIF Mechs/templar.bmp V4H=600054B OURS=201080B +~DIF Mechs/thanatos.bmp V4H=600054B OURS=201080B +~DIF Mechs/thor.bmp V4H=600054B OURS=201080B +~DIF Mechs/Thumbs.db V4H=79872B OURS=140800B +~DIF Mechs/Uller.bmp V4H=600054B OURS=201080B +~DIF Mechs/urbanmech.bmp V4H=600054B OURS=201080B +~DIF Mechs/uziel.bmp V4H=600054B OURS=201080B +~DIF Mechs/vulture.bmp V4H=600054B OURS=201080B +~DIF Mechs/warhammer.bmp V4H=600054B OURS=201080B +~DIF Mechs/wolfhound.bmp V4H=600056B OURS=201080B +~DIF Mechs/zeus.bmp V4H=600054B OURS=201080B +~DIF MFD/rifleman.bmp V4H=43256B OURS=15476B +~DIF MFD/Thumbs.db V4H=8704B OURS=288256B +~DIF missionover.bmp V4H=481080B OURS=481080B +~DIF radar/hud/rifleman.bmp V4H=263224B OURS=263224B +~DIF radar/Thumbs.db V4H=7680B OURS=18432B +~DIF Thumbs.db V4H=30208B OURS=45056B diff --git a/MW4COMPARE/reports/hshoriginal-diff.txt b/MW4COMPARE/reports/hshoriginal-diff.txt new file mode 100644 index 00000000..0e8810ae --- /dev/null +++ b/MW4COMPARE/reports/hshoriginal-diff.txt @@ -0,0 +1,206 @@ +# V4HORIG=447 files, OURS=456 files | identical=275 differ=148 V4HORIG_only=24 OURS_only=33 ++V4HORIG decals/DDC.bmp 30056 ++V4HORIG hud/Champion.bmp 263224 ++V4HORIG hud/Convert.txt 586 ++V4HORIG hud/Dasher.bmp 263224 ++V4HORIG hud/jenner2c.bmp 263224 ++V4HORIG hud/Marauder.bmp 263224 ++V4HORIG Mechs/battlemasteriic.bmp 600054 ++V4HORIG Mechs/champion.bmp 600054 ++V4HORIG Mechs/Dasher.bmp 600054 ++V4HORIG Mechs/jenner2c.bmp 600054 ++V4HORIG Mechs/mad cat mk.ii.bmp 600054 ++V4HORIG Mechs/marauder.bmp 600054 ++V4HORIG MFD/assassinii.bmp 43256 ++V4HORIG MFD/champion.bmp 43254 ++V4HORIG MFD/Dasher.bmp 43254 ++V4HORIG MFD/jenner2c.bmp 43254 ++V4HORIG MFD/mad cat mkii.bmp 43254 ++V4HORIG MFD/mad cat.bmp 43254 ++V4HORIG MFD/marauder.bmp 43254 ++V4HORIG radar/hud/champion.bmp 263224 ++V4HORIG radar/hud/Convert.txt 579 ++V4HORIG radar/hud/dasher.bmp 263224 ++V4HORIG radar/hud/Jenner2c.bmp 263224 ++V4HORIG radar/hud/marauder.bmp 263224 +-OURS decals/decal_46.bmp 11080 +-OURS decals/decal_47.bmp 30056 +-OURS decals/decal_49.bmp 30056 +-OURS hud/assassin2.bmp 263224 +-OURS hud/behemoth.bmp 263224 +-OURS hud/behemothii.bmp 263224 +-OURS Mechs/battlemaster iic.bmp 201068 +-OURS Mechs/behemoth ii.bmp 201080 +-OURS Mechs/behemoth.bmp 201080 +-OURS Mechs/black hawk.bmp 201080 +-OURS Mechs/longbow.bmp 201080 +-OURS Mechs/mad cat mkii.bmp 201080 +-OURS Mechs/solitare.bmp 201080 +-OURS Mechs/victor.bmp 201080 +-OURS MFD/assassin2.bmp 15480 +-OURS MFD/behemoth.bmp 15480 +-OURS MFD/behemothii.bmp 15480 +-OURS MFD/fafnir.bmp 15480 +-OURS MFD/flea.bmp 15480 +-OURS MFD/gladiator.bmp 15480 +-OURS MFD/hellspawn.bmp 15480 +-OURS MFD/kodiak.bmp 15480 +-OURS MFD/longbow.bmp 15480 +-OURS MFD/madcat.bmp 15476 +-OURS MFD/madcat2.bmp 43256 +-OURS MFD/sunder.bmp 15480 +-OURS MFD/Thumbs.db 288256 +-OURS MFD/victor.bmp 15480 +-OURS radar/hud/assassin2.bmp 263224 +-OURS radar/hud/behemoth.bmp 263224 +-OURS radar/hud/behemothii.bmp 263224 +-OURS radar/hud/Thumbs.db 8704 +-OURS radar/Thumbs.db 18432 +~DIF decals/Thumbs.db V4HORIG=8704B OURS=8704B +~DIF hud/annihilator.bmp V4HORIG=263224B OURS=263224B +~DIF hud/archer.bmp V4HORIG=263224B OURS=263224B +~DIF hud/ares.bmp V4HORIG=263224B OURS=263224B +~DIF hud/argus.bmp V4HORIG=263224B OURS=263224B +~DIF hud/avatar.bmp V4HORIG=263224B OURS=263224B +~DIF hud/battlemaster.bmp V4HORIG=263224B OURS=263224B +~DIF hud/battlemasteriic.bmp V4HORIG=263224B OURS=263224B +~DIF hud/blackhawk.bmp V4HORIG=263224B OURS=263224B +~DIF hud/Fafnir.bmp V4HORIG=263224B OURS=263224B +~DIF hud/flea.bmp V4HORIG=263224B OURS=263224B +~DIF hud/gladiator.bmp V4HORIG=263224B OURS=263224B +~DIF hud/hellspawn.bmp V4HORIG=263224B OURS=263224B +~DIF hud/kodiak.bmp V4HORIG=263224B OURS=263224B +~DIF hud/longbow.bmp V4HORIG=263224B OURS=263224B +~DIF hud/rifleman.bmp V4HORIG=263224B OURS=263224B +~DIF hud/sunder.bmp V4HORIG=263224B OURS=263224B +~DIF hud/Thumbs.db V4HORIG=8192B OURS=830768B +~DIF hud/victor.bmp V4HORIG=263224B OURS=263224B +~DIF hud/warhammer.bmp V4HORIG=263224B OURS=263224B +~DIF logo.bmp V4HORIG=481080B OURS=481080B +~DIF Mechs/annihilator.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/archer.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/arctic wolf.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/ares.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/argus.bmp V4HORIG=600054B OURS=201072B +~DIF Mechs/assassin ii.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/atlas.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/avatar.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/awesome.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/battlemaster.bmp V4HORIG=600054B OURS=201072B +~DIF Mechs/black knight.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/black lanner.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/brigand.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/bushwacker.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/catapult.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/cauldronborn.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/chimera.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/commando.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/cougar.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/cyclops.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/daishi.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/deimos.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/dragon.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/fafnir.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/flea.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/gladiator.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/grizzly.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/Hauptmann.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/hellhound.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/hellspawn.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/highlander.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/hollander ii.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/hunchback.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/kodiak.bmp V4HORIG=600054B OURS=201072B +~DIF Mechs/loki.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/mad cat.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/Masakari.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/mauler.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/nova cat.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/osiris.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/owens.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/puma.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/raven.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/rifleman.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/Ryoken.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/shadow cat.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/sunder.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/templar.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/thanatos.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/thor.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/Thumbs.db V4HORIG=6144B OURS=140800B +~DIF Mechs/Uller.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/urbanmech.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/uziel.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/vulture.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/warhammer.bmp V4HORIG=600054B OURS=201080B +~DIF Mechs/wolfhound.bmp V4HORIG=600056B OURS=201080B +~DIF Mechs/zeus.bmp V4HORIG=600054B OURS=201080B +~DIF MFD/annihilator.bmp V4HORIG=43256B OURS=15480B +~DIF MFD/archer.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/arcticwolf.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/ares.bmp V4HORIG=43256B OURS=15480B +~DIF MFD/argus.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/atlas.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/avatar.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/awesome.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/battlemaster.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/battlemasteriic.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/blackhawk.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/blackknight.bmp V4HORIG=43256B OURS=43256B +~DIF MFD/blacklanner.bmp V4HORIG=43256B OURS=15320B +~DIF MFD/brigand.bmp V4HORIG=43256B OURS=43256B +~DIF MFD/bushwacker.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/catapult.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/cauldronborn.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/chimera.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/commando.bmp V4HORIG=43256B OURS=43256B +~DIF MFD/cougar.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/cyclops.bmp V4HORIG=43256B OURS=43256B +~DIF MFD/daishi.bmp V4HORIG=43256B OURS=15480B +~DIF MFD/deimos.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/dragon.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/grizzly.bmp V4HORIG=43256B OURS=43256B +~DIF MFD/hauptmann.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/hellhound.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/highlander.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/hollanderii.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/hunchback.bmp V4HORIG=43256B OURS=43256B +~DIF MFD/loki.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/masakari.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/mauler.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/novacat.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/osiris.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/owens.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/puma.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/raven.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/rifleman.bmp V4HORIG=43256B OURS=15476B +~DIF MFD/ryoken.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/shadowcat.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/solitaire.bmp V4HORIG=43256B OURS=15480B +~DIF MFD/templar.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/thanatos.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/thor.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/uller.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/urbanmech.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/uziel.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/vulture.bmp V4HORIG=43254B OURS=43256B +~DIF MFD/warhammer.bmp V4HORIG=43254B OURS=15480B +~DIF MFD/wolfhound.bmp V4HORIG=43256B OURS=43256B +~DIF MFD/zeus.bmp V4HORIG=43254B OURS=43256B +~DIF missionover.bmp V4HORIG=481080B OURS=481080B +~DIF radar/hud/annihilator.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/archer.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/ares.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/argus.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/avatar.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/battlemaster.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/battlemasteriic.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/blackhawk.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/Fafnir.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/hellspawn.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/kodiak.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/longbow.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/rifleman.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/victor.bmp V4HORIG=263224B OURS=263224B +~DIF radar/hud/warhammer.bmp V4HORIG=263224B OURS=263224B +~DIF Thumbs.db V4HORIG=8192B OURS=45056B diff --git a/MW4COMPARE/reports/maps-decoded-diff.txt b/MW4COMPARE/reports/maps-decoded-diff.txt new file mode 100644 index 00000000..30870442 --- /dev/null +++ b/MW4COMPARE/reports/maps-decoded-diff.txt @@ -0,0 +1,75 @@ +### alpine02.mw4 +# entries: A=458 B=458 A_only=0 B_only=0 decoded-differ=0 +### arctic04.mw4 +# entries: A=341 B=341 A_only=0 B_only=0 decoded-differ=0 +### arctic06.mw4 +# entries: A=342 B=342 A_only=0 B_only=0 decoded-differ=0 +### colsm01.mw4 +# entries: A=182 B=182 A_only=0 B_only=0 decoded-differ=1 +~ maps/colsm01/colsm01.data{gamemodel} A=184B B=184B +### colsm02.mw4 +# entries: A=248 B=248 A_only=0 B_only=0 decoded-differ=2 +~ maps/colsm02/colsm02.instance A=100B B=100B +~ textures/skysc2f1.tga A=196652B B=196652B +### darklord.mw4 +# entries: A=135 B=135 A_only=0 B_only=0 decoded-differ=0 +### desert07.mw4 +# entries: A=399 B=399 A_only=0 B_only=0 decoded-differ=1 +~ maps/desert07/desert07.data{gamemodel} A=184B B=184B +### fact01.mw4 +# entries: A=201 B=201 A_only=0 B_only=0 decoded-differ=1 +~ maps/fact01/fact01.data{gamemodel} A=184B B=184B +### firestorm.mw4 +# entries: A=63 B=63 A_only=0 B_only=0 decoded-differ=1 +~ maps/firestorm/firestorm.data{gamemodel} A=184B B=184B +### grassland.mw4 +# entries: A=287 B=287 A_only=0 B_only=0 decoded-differ=0 +### hotplate.mw4 +# entries: A=219 B=219 A_only=0 B_only=0 decoded-differ=1 +~ maps/hotplate/hotplate.data{gamemodel} A=184B B=184B +### ice3.mw4 +# entries: A=219 B=219 A_only=0 B_only=0 decoded-differ=1 +~ maps/ice3/ice3.data{gamemodel} A=184B B=184B +### jung02.mw4 +# entries: A=174 B=174 A_only=0 B_only=0 decoded-differ=1 +~ maps/jung02/jung02.data{gamemodel} A=184B B=184B +### lunar01.mw4 +# entries: A=249 B=249 A_only=0 B_only=0 decoded-differ=1 +~ maps/lunar01/lunar01.data{gamemodel} A=184B B=184B +### minerl03.mw4 +# entries: A=358 B=358 A_only=0 B_only=0 decoded-differ=1 +~ maps/minerl03/minerl03.data{gamemodel} A=184B B=184B +### nazca.mw4 +# entries: A=224 B=224 A_only=0 B_only=0 decoded-differ=1 +~ maps/nazca/nazca.data{gamemodel} A=184B B=184B +### palace01.mw4 +# entries: A=244 B=244 A_only=0 B_only=0 decoded-differ=1 +~ maps/palace01/palace01.data{gamemodel} A=184B B=184B +### peaks.mw4 +# entries: A=219 B=219 A_only=0 B_only=0 decoded-differ=1 +~ maps/peaks/peaks.data{gamemodel} A=184B B=184B +### reduex.mw4 +# entries: A=248 B=248 A_only=0 B_only=0 decoded-differ=1 +~ maps/reduex/reduex.data{gamemodel} A=184B B=184B +### rookiearena2-terrain-v1.mw4 +# entries: A=63 B=63 A_only=0 B_only=0 decoded-differ=1 +~ maps/rookiearena2-terrain-v1/rookiearena2-terrain-v1.data{gamemodel} A=184B B=184B +### scrub02.mw4 +# entries: A=355 B=355 A_only=0 B_only=0 decoded-differ=1 +~ maps/scrub02/scrub02.data{gamemodel} A=184B B=184B +### stormcanyon.mw4 +# entries: A=128 B=128 A_only=0 B_only=0 decoded-differ=1 +~ maps/stormcanyon/stormcanyon.data{gamemodel} A=184B B=184B +### stormcanyonsiege.mw4 +# entries: A=137 B=137 A_only=0 B_only=0 decoded-differ=1 +~ maps/stormcanyonsiege/stormcanyonsiege.data{gamemodel} A=184B B=184B +### swamp01.mw4 +# entries: A=488 B=488 A_only=0 B_only=0 decoded-differ=2 +~ maps/swamp01/crater.data{gamemodel} A=32B B=32B +~ maps/swamp01/swamp01.data{gamemodel} A=184B B=184B +### urban01.mw4 +# entries: A=446 B=446 A_only=0 B_only=0 decoded-differ=0 +### urban02.mw4 +# entries: A=396 B=396 A_only=0 B_only=0 decoded-differ=0 +### urban05.mw4 +# entries: A=396 B=396 A_only=0 B_only=0 decoded-differ=0 diff --git a/MW4COMPARE/reports/missions-decoded-diff.txt b/MW4COMPARE/reports/missions-decoded-diff.txt new file mode 100644 index 00000000..117f3007 --- /dev/null +++ b/MW4COMPARE/reports/missions-decoded-diff.txt @@ -0,0 +1,173 @@ +### bigcity.mw4 +# entries: A=80 B=80 A_only=0 B_only=0 decoded-differ=3 +~ missions/bigcity/bigcity.contents A=585438B B=585438B +~ missions/bigcity/bigcity.lights A=131340B B=131340B +~ missions/bigcity/bigcitynight.lights A=131340B B=131340B +### cantina.mw4 +# entries: A=31 B=31 A_only=0 B_only=0 decoded-differ=3 +~ missions/cantina/cantina.contents A=80886B B=80886B +~ missions/cantina/cantina.lights A=131347B B=131347B +~ missions/cantina/cantinanight.lights A=131347B B=131347B +### cantinasiege.mw4 +# entries: A=50 B=50 A_only=0 B_only=0 decoded-differ=3 +~ missions/cantinasiege/cantinasiege.contents A=91546B B=91546B +~ missions/cantinasiege/cantinasiege.lights A=131352B B=131352B +~ missions/cantinasiege/cantinasiegenight.lights A=131352B B=131352B +### coliseum.mw4 +# entries: A=47 B=47 A_only=0 B_only=0 decoded-differ=3 +~ missions/coliseum/coliseum.contents A=52954B B=52954B +~ missions/coliseum/coliseum.lights A=33033B B=33033B +~ missions/coliseum/coliseumnight.lights A=33033B B=33033B +### cpark.mw4 +# entries: A=31 B=31 A_only=0 B_only=0 decoded-differ=3 +~ missions/centralpark/centralpark.contents A=321566B B=321566B +~ missions/centralpark/centralpark.lights A=131355B B=131355B +~ missions/centralpark/centralparknight.lights A=131355B B=131355B +### dustbowl.mw4 +# entries: A=28 B=28 A_only=0 B_only=0 decoded-differ=3 +~ missions/dustbowl/dustbowl.contents A=10794B B=10794B +~ missions/dustbowl/dustbowl.lights A=131433B B=131433B +~ missions/dustbowl/dustbowlnight.lights A=131433B B=131433B +### factory.mw4 +# entries: A=25 B=25 A_only=0 B_only=0 decoded-differ=2 +~ missions/factory/factory.contents A=136990B B=136990B +~ missions/factory/factory.lights A=33042B B=33042B +### fbite.mw4 +# entries: A=32 B=32 A_only=0 B_only=0 decoded-differ=3 +~ missions/frostbite/frostbite.contents A=91850B B=91850B +~ missions/frostbite/frostbite.lights A=131351B B=131351B +~ missions/frostbite/frostbitenight.lights A=131351B B=131351B +### freezer.mw4 +# entries: A=165 B=135 A_only=30 B_only=0 decoded-differ=4 ++A audio/vo/generic/mp/ahh.wav 54966B ++A audio/vo/generic/mp/ahh.wav{handle} 10B ++A audio/vo/generic/mp/aww.wav 11958B ++A audio/vo/generic/mp/aww.wav{handle} 10B ++A audio/vo/generic/mp/boo1.wav 97718B ++A audio/vo/generic/mp/boo1.wav{handle} 10B ++A audio/vo/generic/mp/boo2.wav 131510B ++A audio/vo/generic/mp/boo2.wav{handle} 10B ++A audio/vo/generic/mp/boo3.wav 138166B ++A audio/vo/generic/mp/boo3.wav{handle} 10B ++A audio/vo/generic/mp/cheer1.wav 200118B ++A audio/vo/generic/mp/cheer1.wav{handle} 10B ++A audio/vo/generic/mp/cheer2.wav 102838B ++A audio/vo/generic/mp/cheer2.wav{handle} 10B ++A audio/vo/generic/mp/cheer3.wav 95670B ++A audio/vo/generic/mp/cheer3.wav{handle} 10B ++A audio/vo/generic/mp/cheer4.wav 152246B ++A audio/vo/generic/mp/cheer4.wav{handle} 10B ++A audio/vo/generic/mp/cheer5.wav 92086B ++A audio/vo/generic/mp/cheer5.wav{handle} 10B ++A audio/vo/generic/mp/cheer8.wav 14076B ++A audio/vo/generic/mp/cheer8.wav{handle} 10B ++A audio/vo/generic/mp/cheer9.wav 63670B ++A audio/vo/generic/mp/cheer9.wav{handle} 10B ++A audio/vo/generic/mp/jeers.wav 127414B ++A audio/vo/generic/mp/jeers.wav{handle} 10B ++A audio/vo/generic/mp/roar.wav 9654B ++A audio/vo/generic/mp/roar.wav{handle} 10B ++A audio/vo/generic/mp/stadamb.wav 23478B ++A audio/vo/generic/mp/stdamb.wav{handle} 10B +~ missions/freezer/freezer.contents A=16338B B=16338B +~ missions/freezer/freezer.lights A=189B B=189B +~ missions/freezer/freezernight.lights A=189B B=189B +~ missions/freezer/scripts/freezer_teamattrition.abl A=10710B B=832B +### gbait.mw4 +# entries: A=32 B=32 A_only=0 B_only=0 decoded-differ=3 +~ missions/gatorbait/gatorbait.contents A=131102B B=131102B +~ missions/gatorbait/gatorbait.lights A=131344B B=131344B +~ missions/gatorbait/gatorbaitnight.lights A=131344B B=131344B +### ghosthighway.mw4 +# entries: A=30 B=30 A_only=0 B_only=0 decoded-differ=3 +~ missions/ghosthighway/ghosthighway.contents A=46262B B=46262B +~ missions/ghosthighway/ghosthighway.lights A=131356B B=131356B +~ missions/ghosthighway/ghosthighwaynight.lights A=131356B B=131356B +### grassland.mw4 +# entries: A=133 B=133 A_only=0 B_only=0 decoded-differ=3 +~ missions/grassland/grassland.contents A=191662B B=191662B +~ missions/grassland/grassland.lights A=189B B=189B +~ missions/grassland/grasslandnight.lights A=189B B=189B +### hideaway.mw4 +# entries: A=131 B=131 A_only=0 B_only=0 decoded-differ=3 +~ missions/hideaway/hideaway.contents A=26798B B=26798B +~ missions/hideaway/hideaway.lights A=33136B B=33136B +~ missions/hideaway/hideawaynight.lights A=33136B B=33136B +### hotplate.mw4 +# entries: A=129 B=129 A_only=0 B_only=0 decoded-differ=3 +~ missions/hotplate/hotplate.contents A=134446B B=134446B +~ missions/hotplate/hotplate.lights A=189B B=189B +~ missions/hotplate/hotplatenight.lights A=189B B=189B +### icity.mw4 +# entries: A=26 B=26 A_only=0 B_only=0 decoded-differ=3 +~ missions/innercity/innercity.contents A=228938B B=228938B +~ missions/innercity/innercity.lights A=131351B B=131351B +~ missions/innercity/innercitynight.lights A=131351B B=131351B +### jungle.mw4 +# entries: A=36 B=36 A_only=0 B_only=0 decoded-differ=3 +~ missions/jungle/jungle.contents A=109430B B=109430B +~ missions/jungle/jungle.lights A=33121B B=33121B +~ missions/jungle/junglenight.lights A=33030B B=33030B +### lunacy.mw4 +# entries: A=25 B=25 A_only=0 B_only=0 decoded-differ=3 +~ missions/lunacy/lunacy.contents A=33574B B=33574B +~ missions/lunacy/lunacy.lights A=74085B B=74085B +~ missions/lunacy/lunacynight.lights A=74085B B=74085B +### nazca.mw4 +# entries: A=134 B=134 A_only=0 B_only=0 decoded-differ=3 +~ missions/nazca/nazca.contents A=35498B B=35498B +~ missions/nazca/nazca.lights A=189B B=189B +~ missions/nazca/nazcanight.lights A=189B B=189B +### peaks.mw4 +# entries: A=129 B=129 A_only=0 B_only=0 decoded-differ=3 +~ missions/peaks/peaks.contents A=103198B B=103198B +~ missions/peaks/peaks.lights A=189B B=189B +~ missions/peaks/peaksnight.lights A=189B B=189B +### pgates.mw4 +# entries: A=155 B=155 A_only=0 B_only=0 decoded-differ=3 +~ missions/palacegates/palacegates.contents A=141158B B=141158B +~ missions/palacegates/palacegates.lights A=74095B B=74095B +~ missions/palacegates/palacegatesnight.lights A=74095B B=74095B +### reduex.mw4 +# entries: A=128 B=128 A_only=0 B_only=0 decoded-differ=3 +~ missions/reduex/reduex.contents A=183102B B=183102B +~ missions/reduex/reduex.lights A=74081B B=74081B +~ missions/reduex/reduexnight.lights A=98B B=98B +### reduexsiege.mw4 +# entries: A=137 B=137 A_only=0 B_only=0 decoded-differ=3 +~ missions/reduexsiege/reduexsiege.contents A=225374B B=225374B +~ missions/reduexsiege/reduexsiege.lights A=74081B B=74081B +~ missions/reduexsiege/reduexsiegenight.lights A=98B B=98B +### scarabstronghold.mw4 +# entries: A=123 B=123 A_only=0 B_only=0 decoded-differ=3 +~ missions/scarabstronghold/scarabstronghold.contents A=45846B B=45846B +~ missions/scarabstronghold/scarabstronghold.lights A=189B B=189B +~ missions/scarabstronghold/scarabstrongholdnight.lights A=98B B=98B +### snowjob.mw4 +# entries: A=29 B=29 A_only=0 B_only=0 decoded-differ=3 +~ missions/snowjob/snowjob.contents A=19570B B=19570B +~ missions/snowjob/snowjob.lights A=131347B B=131347B +~ missions/snowjob/snowjobnight.lights A=131347B B=131347B +### stormcanyon.mw4 +# entries: A=181 B=181 A_only=0 B_only=0 decoded-differ=2 +~ missions/stormcanyon/stormcanyon.contents A=96058B B=96058B +~ missions/stormcanyon/stormcanyonnight.lights A=189B B=189B +### stormcanyonsiege.mw4 +# entries: A=163 B=163 A_only=0 B_only=0 decoded-differ=2 +~ missions/stormcanyonsiege/stormcanyonsiege.contents A=188522B B=188522B +~ missions/stormcanyonsiege/stormcanyonsiegenight.lights A=189B B=189B +### tline.mw4 +# entries: A=31 B=31 A_only=0 B_only=0 decoded-differ=3 +~ missions/timberline/timberline.contents A=119674B B=119674B +~ missions/timberline/timberline.lights A=131346B B=131346B +~ missions/timberline/timberlinenight.lights A=131346B B=131346B +### tribeincursionmission.mw4 +# entries: A=79 B=79 A_only=0 B_only=0 decoded-differ=3 +~ missions/tribeincursionmission/tribeincursionmission.contents A=156486B B=156486B +~ missions/tribeincursionmission/tribeincursionmission.lights A=189B B=189B +~ missions/tribeincursionmission/tribeincursionmissionnight.lights A=189B B=189B +### tribeincursion.mw4 +# entries: A=71 B=71 A_only=0 B_only=0 decoded-differ=3 +~ missions/tribeincursion/tribeincursion.contents A=126646B B=126646B +~ missions/tribeincursion/tribeincursion.lights A=189B B=189B +~ missions/tribeincursion/tribeincursionnight.lights A=189B B=189B diff --git a/MW4COMPARE/reports/packages-entrydiff.txt b/MW4COMPARE/reports/packages-entrydiff.txt new file mode 100644 index 00000000..dd76a183 --- /dev/null +++ b/MW4COMPARE/reports/packages-entrydiff.txt @@ -0,0 +1,2140 @@ +== core.mw4: V4H=12709 OURS=11955 | names: common=11955 V4H_only=754 OURS_only=0 | blobs: identical=3440 V4H_uniq=9269 OURS_uniq=8515 + +V4H mechs/champion/armaturedata/chp_hip.data (12B) + +V4H mechs/champion/armaturedata/chp_hip.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_hip.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_lbtoe.data (12B) + +V4H mechs/champion/armaturedata/chp_lbtoe.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_lbtoe.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_ldleg.data (12B) + +V4H mechs/champion/armaturedata/chp_ldleg.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_ldleg.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_lfoot.data (12B) + +V4H mechs/champion/armaturedata/chp_lfoot.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_lfoot.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_lftoe.data (12B) + +V4H mechs/champion/armaturedata/chp_lftoe.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_lftoe.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_lgun.data (12B) + +V4H mechs/champion/armaturedata/chp_lgun.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_lgun.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_luarm.data (12B) + +V4H mechs/champion/armaturedata/chp_luarm.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_luarm.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_luleg.data (12B) + +V4H mechs/champion/armaturedata/chp_luleg.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_luleg.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_rbtoe.data (12B) + +V4H mechs/champion/armaturedata/chp_rbtoe.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_rbtoe.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_rdleg.data (12B) + +V4H mechs/champion/armaturedata/chp_rdleg.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_rdleg.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_rfoot.data (12B) + +V4H mechs/champion/armaturedata/chp_rfoot.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_rfoot.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_rftoe.data (12B) + +V4H mechs/champion/armaturedata/chp_rftoe.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_rftoe.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_rgun.data (12B) + +V4H mechs/champion/armaturedata/chp_rgun.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_rgun.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_ruarm.data (12B) + +V4H mechs/champion/armaturedata/chp_ruarm.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_ruarm.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_ruleg.data (12B) + +V4H mechs/champion/armaturedata/chp_ruleg.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_ruleg.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_specialone.data (12B) + +V4H mechs/champion/armaturedata/chp_specialone.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_specialone.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_specialtwo.data (12B) + +V4H mechs/champion/armaturedata/chp_specialtwo.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_specialtwo.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/chp_torso.data (12B) + +V4H mechs/champion/armaturedata/chp_torso.data{element} (100B) + +V4H mechs/champion/armaturedata/chp_torso.data{gamemodel} (80B) + +V4H mechs/champion/armaturedata/joint_cage.data (12B) + +V4H mechs/champion/armaturedata/joint_cage.data{element} (100B) + +V4H mechs/champion/armaturedata/joint_cage.data{gamemodel} (80B) + +V4H mechs/champion/armaturevideo/chp_hip.video (406B) + +V4H mechs/champion/armaturevideo/chp_lbtoe.video (406B) + +V4H mechs/champion/armaturevideo/chp_ldleg.video (406B) + +V4H mechs/champion/armaturevideo/chp_lfoot.video (406B) + +V4H mechs/champion/armaturevideo/chp_lftoe.video (406B) + +V4H mechs/champion/armaturevideo/chp_lgun.video (285B) + +V4H mechs/champion/armaturevideo/chp_luarm.video (406B) + +V4H mechs/champion/armaturevideo/chp_luleg.video (406B) + +V4H mechs/champion/armaturevideo/chp_rbtoe.video (406B) + +V4H mechs/champion/armaturevideo/chp_rdleg.video (406B) + +V4H mechs/champion/armaturevideo/chp_rfoot.video (406B) + +V4H mechs/champion/armaturevideo/chp_rftoe.video (406B) + +V4H mechs/champion/armaturevideo/chp_rgun.video (285B) + +V4H mechs/champion/armaturevideo/chp_ruarm.video (406B) + +V4H mechs/champion/armaturevideo/chp_ruleg.video (406B) + +V4H mechs/champion/armaturevideo/chp_specialone.video (406B) + +V4H mechs/champion/armaturevideo/chp_specialtwo.video (406B) + +V4H mechs/champion/armaturevideo/chp_torso.video (683B) + +V4H mechs/champion/armaturevideo/joint_cage.video (646B) + +V4H mechs/champion/champion.contents (282B) + +V4H mechs/champion/champion.contents[joint_cage]{sites} (124B) + +V4H mechs/champion/champion.contents[joint_hip]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_hipabove]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_hipbelow]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_lankle]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_lbelowankle]{armature} (842B) + +V4H mechs/champion/champion.contents[joint_lbelowankle]{sites} (39B) + +V4H mechs/champion/champion.contents[joint_ldleg]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_ldleg]{sites} (43B) + +V4H mechs/champion/champion.contents[joint_lefttorsofront]{sites} (44B) + +V4H mechs/champion/champion.contents[joint_lgun]{sites} (86B) + +V4H mechs/champion/champion.contents[joint_lgunabove]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_luarm]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_luleg]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_rankle]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_rbelowankle]{armature} (842B) + +V4H mechs/champion/champion.contents[joint_rbelowankle]{sites} (39B) + +V4H mechs/champion/champion.contents[joint_rdleg]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_rdleg]{sites} (43B) + +V4H mechs/champion/champion.contents[joint_rgun]{sites} (130B) + +V4H mechs/champion/champion.contents[joint_rgunabove]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_righttorsofront]{sites} (44B) + +V4H mechs/champion/champion.contents[joint_root]{armature} (842B) + +V4H mechs/champion/champion.contents[joint_ruarm]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_ruleg]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_specialone]{sites} (46B) + +V4H mechs/champion/champion.contents[joint_specialtwo]{sites} (46B) + +V4H mechs/champion/champion.contents[joint_torso]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_torsoabove]{armature} (3082B) + +V4H mechs/champion/champion.contents[joint_torsoabove]{sites} (521B) + +V4H mechs/champion/champion.contents[joint_torsobelow]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_vel]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_world]{armature} (282B) + +V4H mechs/champion/champion.contents[joint_world]{sites} (40B) + +V4H mechs/champion/champion.damage (1688B) + +V4H mechs/champion/champion.data (12B) + +V4H mechs/champion/champion.data[shadow] (117B) + +V4H mechs/champion/champion.data{element} (100B) + +V4H mechs/champion/champion.data{footsteps} (96B) + +V4H mechs/champion/champion.data{gamemodel} (1636B) + +V4H mechs/champion/champion.data{hierarchicalobb} (5607B) + +V4H mechs/champion/champion.data{solidobb} (78B) + +V4H mechs/champion/champion.engine (12B) + +V4H mechs/champion/champion.engine{element} (100B) + +V4H mechs/champion/champion.engine{gamemodel} (64B) + +V4H mechs/champion/champion.instance (344B) + +V4H mechs/champion/champion.subsystems (4698B) + +V4H mechs/champion/champion.torso (12B) + +V4H mechs/champion/champion.torso{element} (100B) + +V4H mechs/champion/champion.torso{gamemodel} (1608B) + +V4H mechs/champion_destroyed/champion_stroyed.data (12B) + +V4H mechs/champion_destroyed/champion_stroyed.data{element} (100B) + +V4H mechs/champion_destroyed/champion_stroyed.data{gamemodel} (32B) + +V4H mechs/champion_destroyed/champion_stroyed.video (285B) + +V4H mechs/champion_destroyed/champion_stroyed_solid.obb (78B) + +V4H mechs/dasher/armaturedata/das_hip.data (12B) + +V4H mechs/dasher/armaturedata/das_hip.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_hip.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_ldleg.data (12B) + +V4H mechs/dasher/armaturedata/das_ldleg.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_ldleg.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_lfoot.data (12B) + +V4H mechs/dasher/armaturedata/das_lfoot.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_lfoot.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_lgun.data (12B) + +V4H mechs/dasher/armaturedata/das_lgun.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_lgun.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_ltoe.data (12B) + +V4H mechs/dasher/armaturedata/das_ltoe.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_ltoe.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_luarm.data (12B) + +V4H mechs/dasher/armaturedata/das_luarm.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_luarm.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_luleg.data (12B) + +V4H mechs/dasher/armaturedata/das_luleg.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_luleg.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_rdleg.data (12B) + +V4H mechs/dasher/armaturedata/das_rdleg.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_rdleg.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_rfoot.data (12B) + +V4H mechs/dasher/armaturedata/das_rfoot.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_rfoot.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_rgun.data (12B) + +V4H mechs/dasher/armaturedata/das_rgun.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_rgun.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_rtoe.data (12B) + +V4H mechs/dasher/armaturedata/das_rtoe.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_rtoe.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_ruarm.data (12B) + +V4H mechs/dasher/armaturedata/das_ruarm.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_ruarm.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_ruleg.data (12B) + +V4H mechs/dasher/armaturedata/das_ruleg.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_ruleg.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/das_torso.data (12B) + +V4H mechs/dasher/armaturedata/das_torso.data{element} (100B) + +V4H mechs/dasher/armaturedata/das_torso.data{gamemodel} (80B) + +V4H mechs/dasher/armaturedata/joint_cage.data (12B) + +V4H mechs/dasher/armaturedata/joint_cage.data{element} (100B) + +V4H mechs/dasher/armaturedata/joint_cage.data{gamemodel} (80B) + +V4H mechs/dasher/armaturevideo/das_hip.video (406B) + +V4H mechs/dasher/armaturevideo/das_ldleg.video (406B) + +V4H mechs/dasher/armaturevideo/das_lfoot.video (406B) + +V4H mechs/dasher/armaturevideo/das_lgun.video (406B) + +V4H mechs/dasher/armaturevideo/das_ltoe.video (406B) + +V4H mechs/dasher/armaturevideo/das_luarm.video (406B) + +V4H mechs/dasher/armaturevideo/das_luleg.video (406B) + +V4H mechs/dasher/armaturevideo/das_rdleg.video (406B) + +V4H mechs/dasher/armaturevideo/das_rfoot.video (406B) + +V4H mechs/dasher/armaturevideo/das_rgun.video (285B) + +V4H mechs/dasher/armaturevideo/das_rtoe.video (406B) + +V4H mechs/dasher/armaturevideo/das_ruarm.video (406B) + +V4H mechs/dasher/armaturevideo/das_ruleg.video (406B) + +V4H mechs/dasher/armaturevideo/das_torso.video (683B) + +V4H mechs/dasher/armaturevideo/joint_cage.video (646B) + +V4H mechs/dasher/dasher.contents (282B) + +V4H mechs/dasher/dasher.contents[joint_cage]{sites} (124B) + +V4H mechs/dasher/dasher.contents[joint_head]{sites} (41B) + +V4H mechs/dasher/dasher.contents[joint_hip]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_hipabove]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_hipbelow]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_lankle]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_lbelowankle]{armature} (562B) + +V4H mechs/dasher/dasher.contents[joint_lbelowankle]{sites} (39B) + +V4H mechs/dasher/dasher.contents[joint_ldleg]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_lefttorsofront]{sites} (44B) + +V4H mechs/dasher/dasher.contents[joint_lgun]{sites} (172B) + +V4H mechs/dasher/dasher.contents[joint_lgunabove]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_luarm]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_luleg]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_rankle]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_rbelowankle]{armature} (562B) + +V4H mechs/dasher/dasher.contents[joint_rbelowankle]{sites} (39B) + +V4H mechs/dasher/dasher.contents[joint_rdleg]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_rgun]{sites} (172B) + +V4H mechs/dasher/dasher.contents[joint_rgunabove]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_righttorsofront]{sites} (44B) + +V4H mechs/dasher/dasher.contents[joint_root]{armature} (842B) + +V4H mechs/dasher/dasher.contents[joint_ruarm]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_ruleg]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_torso]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_torsoabove]{armature} (2802B) + +V4H mechs/dasher/dasher.contents[joint_torsoabove]{sites} (393B) + +V4H mechs/dasher/dasher.contents[joint_torsobelow]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_vel]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_world]{armature} (282B) + +V4H mechs/dasher/dasher.contents[joint_world]{sites} (40B) + +V4H mechs/dasher/dasher.damage (1356B) + +V4H mechs/dasher/dasher.data (12B) + +V4H mechs/dasher/dasher.data[shadow] (117B) + +V4H mechs/dasher/dasher.data{element} (100B) + +V4H mechs/dasher/dasher.data{footsteps} (96B) + +V4H mechs/dasher/dasher.data{gamemodel} (1636B) + +V4H mechs/dasher/dasher.data{hierarchicalobb} (4928B) + +V4H mechs/dasher/dasher.data{solidobb} (78B) + +V4H mechs/dasher/dasher.engine (12B) + +V4H mechs/dasher/dasher.engine{element} (100B) + +V4H mechs/dasher/dasher.engine{gamemodel} (64B) + +V4H mechs/dasher/dasher.instance (344B) + +V4H mechs/dasher/dasher.subsystems (2238B) + +V4H mechs/dasher/dasher.torso (12B) + +V4H mechs/dasher/dasher.torso{element} (100B) + +V4H mechs/dasher/dasher.torso{gamemodel} (1608B) + +V4H mechs/dasher_destroyed/dasher_destroyed.data (12B) + +V4H mechs/dasher_destroyed/dasher_destroyed.data{element} (100B) + +V4H mechs/dasher_destroyed/dasher_destroyed.data{gamemodel} (32B) + +V4H mechs/dasher_destroyed/dasher_destroyed.video (285B) + +V4H mechs/dasher_destroyed/dasher_destroyed_solid.obb (78B) + +V4H mechs/griffin/armaturedata/grf_hip.data (12B) + +V4H mechs/griffin/armaturedata/grf_hip.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_hip.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_lbtoe.data (12B) + +V4H mechs/griffin/armaturedata/grf_lbtoe.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_lbtoe.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_ldleg.data (12B) + +V4H mechs/griffin/armaturedata/grf_ldleg.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_ldleg.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_lfoot.data (12B) + +V4H mechs/griffin/armaturedata/grf_lfoot.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_lfoot.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_lftoe.data (12B) + +V4H mechs/griffin/armaturedata/grf_lftoe.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_lftoe.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_lgun.data (12B) + +V4H mechs/griffin/armaturedata/grf_lgun.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_lgun.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_luarm.data (12B) + +V4H mechs/griffin/armaturedata/grf_luarm.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_luarm.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_luleg.data (12B) + +V4H mechs/griffin/armaturedata/grf_luleg.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_luleg.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_rbtoe.data (12B) + +V4H mechs/griffin/armaturedata/grf_rbtoe.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_rbtoe.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_rdleg.data (12B) + +V4H mechs/griffin/armaturedata/grf_rdleg.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_rdleg.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_rfoot.data (12B) + +V4H mechs/griffin/armaturedata/grf_rfoot.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_rfoot.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_rftoe.data (12B) + +V4H mechs/griffin/armaturedata/grf_rftoe.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_rftoe.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_rgun.data (12B) + +V4H mechs/griffin/armaturedata/grf_rgun.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_rgun.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_ruarm.data (12B) + +V4H mechs/griffin/armaturedata/grf_ruarm.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_ruarm.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_ruleg.data (12B) + +V4H mechs/griffin/armaturedata/grf_ruleg.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_ruleg.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_specialone.data (12B) + +V4H mechs/griffin/armaturedata/grf_specialone.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_specialone.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_specialtwo.data (12B) + +V4H mechs/griffin/armaturedata/grf_specialtwo.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_specialtwo.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/grf_torso.data (12B) + +V4H mechs/griffin/armaturedata/grf_torso.data{element} (100B) + +V4H mechs/griffin/armaturedata/grf_torso.data{gamemodel} (80B) + +V4H mechs/griffin/armaturedata/joint_cage.data (12B) + +V4H mechs/griffin/armaturedata/joint_cage.data{element} (100B) + +V4H mechs/griffin/armaturedata/joint_cage.data{gamemodel} (80B) + +V4H mechs/griffin/armaturevideo/grf_hip.video (406B) + +V4H mechs/griffin/armaturevideo/grf_lbtoe.video (406B) + +V4H mechs/griffin/armaturevideo/grf_ldleg.video (406B) + +V4H mechs/griffin/armaturevideo/grf_lfoot.video (406B) + +V4H mechs/griffin/armaturevideo/grf_lftoe.video (406B) + +V4H mechs/griffin/armaturevideo/grf_lgun.video (285B) + +V4H mechs/griffin/armaturevideo/grf_luarm.video (406B) + +V4H mechs/griffin/armaturevideo/grf_luleg.video (406B) + +V4H mechs/griffin/armaturevideo/grf_rbtoe.video (406B) + +V4H mechs/griffin/armaturevideo/grf_rdleg.video (406B) + +V4H mechs/griffin/armaturevideo/grf_rfoot.video (406B) + +V4H mechs/griffin/armaturevideo/grf_rftoe.video (406B) + +V4H mechs/griffin/armaturevideo/grf_rgun.video (285B) + +V4H mechs/griffin/armaturevideo/grf_ruarm.video (406B) + +V4H mechs/griffin/armaturevideo/grf_ruleg.video (406B) + +V4H mechs/griffin/armaturevideo/grf_specialone.video (406B) + +V4H mechs/griffin/armaturevideo/grf_specialtwo.video (406B) + +V4H mechs/griffin/armaturevideo/grf_torso.video (406B) + +V4H mechs/griffin/armaturevideo/joint_cage.video (646B) + +V4H mechs/griffin/griffin.contents (282B) + +V4H mechs/griffin/griffin.contents[joint_cage]{sites} (124B) + +V4H mechs/griffin/griffin.contents[joint_centertorsofront]{sites} (88B) + +V4H mechs/griffin/griffin.contents[joint_hip]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_hipabove]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_hipbelow]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_lankle]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_lbelowankle]{armature} (842B) + +V4H mechs/griffin/griffin.contents[joint_lbelowankle]{sites} (39B) + +V4H mechs/griffin/griffin.contents[joint_ldleg]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_ldleg]{sites} (43B) + +V4H mechs/griffin/griffin.contents[joint_lefttorsofront]{sites} (46B) + +V4H mechs/griffin/griffin.contents[joint_lgun]{sites} (129B) + +V4H mechs/griffin/griffin.contents[joint_lgunabove]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_luarm]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_luleg]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_rankle]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_rbelowankle]{armature} (842B) + +V4H mechs/griffin/griffin.contents[joint_rbelowankle]{sites} (39B) + +V4H mechs/griffin/griffin.contents[joint_rdleg]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_rdleg]{sites} (43B) + +V4H mechs/griffin/griffin.contents[joint_rgun]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_rgun]{sites} (86B) + +V4H mechs/griffin/griffin.contents[joint_rgunabove]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_righttorsofront]{sites} (46B) + +V4H mechs/griffin/griffin.contents[joint_root]{armature} (842B) + +V4H mechs/griffin/griffin.contents[joint_ruarm]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_ruarm]{sites} (42B) + +V4H mechs/griffin/griffin.contents[joint_ruleg]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_specialone]{sites} (44B) + +V4H mechs/griffin/griffin.contents[joint_specialtwo]{sites} (86B) + +V4H mechs/griffin/griffin.contents[joint_torso]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_torsoabove]{armature} (2802B) + +V4H mechs/griffin/griffin.contents[joint_torsoabove]{sites} (477B) + +V4H mechs/griffin/griffin.contents[joint_torsobelow]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_vel]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_world]{armature} (282B) + +V4H mechs/griffin/griffin.contents[joint_world]{sites} (40B) + +V4H mechs/griffin/griffin.damage (1522B) + +V4H mechs/griffin/griffin.data (12B) + +V4H mechs/griffin/griffin.data[shadow] (117B) + +V4H mechs/griffin/griffin.data{element} (100B) + +V4H mechs/griffin/griffin.data{footsteps} (99B) + +V4H mechs/griffin/griffin.data{gamemodel} (1636B) + +V4H mechs/griffin/griffin.data{hierarchicalobb} (6500B) + +V4H mechs/griffin/griffin.data{solidobb} (78B) + +V4H mechs/griffin/griffin.engine (12B) + +V4H mechs/griffin/griffin.engine{element} (100B) + +V4H mechs/griffin/griffin.engine{gamemodel} (64B) + +V4H mechs/griffin/griffin.instance (344B) + +V4H mechs/griffin/griffin.subsystems (2818B) + +V4H mechs/griffin/griffin.torso (12B) + +V4H mechs/griffin/griffin.torso{element} (100B) + +V4H mechs/griffin/griffin.torso{gamemodel} (1608B) + +V4H mechs/griffin_destroyed/griffin_destroyed.data (12B) + +V4H mechs/griffin_destroyed/griffin_destroyed.data{element} (100B) + +V4H mechs/griffin_destroyed/griffin_destroyed.data{gamemodel} (32B) + +V4H mechs/griffin_destroyed/griffin_destroyed.video (285B) + +V4H mechs/griffin_destroyed/griffin_destroyed_solid.obb (78B) + +V4H mechs/jenner2c/armaturedata/jec_hip.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_hip.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_hip.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_lbtoe.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_lbtoe.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_lbtoe.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_ldleg.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_ldleg.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_ldleg.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_lfoot.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_lfoot.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_lfoot.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_lftoe.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_lftoe.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_lftoe.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_lgun.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_lgun.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_lgun.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_luleg.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_luleg.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_luleg.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_rbtoe.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_rbtoe.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_rbtoe.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_rdleg.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_rdleg.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_rdleg.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_rfoot.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_rfoot.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_rfoot.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_rftoe.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_rftoe.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_rftoe.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_rgun.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_rgun.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_rgun.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_ruleg.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_ruleg.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_ruleg.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/jec_torso.data (12B) + +V4H mechs/jenner2c/armaturedata/jec_torso.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/jec_torso.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturedata/joint_cage.data (12B) + +V4H mechs/jenner2c/armaturedata/joint_cage.data{element} (100B) + +V4H mechs/jenner2c/armaturedata/joint_cage.data{gamemodel} (80B) + +V4H mechs/jenner2c/armaturevideo/jec_hip.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_lbtoe.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_ldleg.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_lfoot.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_lftoe.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_lgun.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_luleg.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_rbtoe.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_rdleg.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_rfoot.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_rftoe.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_rgun.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_ruleg.video (406B) + +V4H mechs/jenner2c/armaturevideo/jec_torso.video (683B) + +V4H mechs/jenner2c/armaturevideo/joint_cage.video (646B) + +V4H mechs/jenner2c/jenner_2c.contents (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_cage]{sites} (124B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_head]{sites} (45B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_hip]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_hipabove]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_hipbelow]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_lankle]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_lbelowankle]{armature} (842B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_lbelowankle]{sites} (39B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_ldleg]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_lgun]{sites} (86B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_lgunabove]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_luarm]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_luleg]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_rankle]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_rbelowankle]{armature} (842B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_rbelowankle]{sites} (39B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_rdleg]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_rgun]{sites} (45B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_rgunabove]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_root]{armature} (842B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_ruarm]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_ruleg]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_torso]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_torsoabove]{armature} (2522B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_torsoabove]{sites} (434B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_torsobelow]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_vel]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_world]{armature} (282B) + +V4H mechs/jenner2c/jenner_2c.contents[joint_world]{sites} (40B) + +V4H mechs/jenner2c/jenner_2c.damage (1356B) + +V4H mechs/jenner2c/jenner_2c.data (12B) + +V4H mechs/jenner2c/jenner_2c.data[shadow] (117B) + +V4H mechs/jenner2c/jenner_2c.data{element} (100B) + +V4H mechs/jenner2c/jenner_2c.data{footsteps} (102B) + +V4H mechs/jenner2c/jenner_2c.data{gamemodel} (1636B) + +V4H mechs/jenner2c/jenner_2c.data{hierarchicalobb} (4943B) + +V4H mechs/jenner2c/jenner_2c.data{solidobb} (78B) + +V4H mechs/jenner2c/jenner_2c.engine (12B) + +V4H mechs/jenner2c/jenner_2c.engine{element} (100B) + +V4H mechs/jenner2c/jenner_2c.engine{gamemodel} (64B) + +V4H mechs/jenner2c/jenner_2c.instance (344B) + +V4H mechs/jenner2c/jenner_2c.subsystems (2562B) + +V4H mechs/jenner2c/jenner_2c.torso (12B) + +V4H mechs/jenner2c/jenner_2c.torso{element} (100B) + +V4H mechs/jenner2c/jenner_2c.torso{gamemodel} (1608B) + +V4H mechs/jenner2c_destroyed/jenner2c_destroyed.data (12B) + +V4H mechs/jenner2c_destroyed/jenner2c_destroyed.data{element} (100B) + +V4H mechs/jenner2c_destroyed/jenner2c_destroyed.data{gamemodel} (32B) + +V4H mechs/jenner2c_destroyed/jenner2c_destroyed.video (285B) + +V4H mechs/jenner2c_destroyed/jenner2c_destroyed_solid.obb (78B) + +V4H mechs/marauder/armaturedata/joint_cage.data (12B) + +V4H mechs/marauder/armaturedata/joint_cage.data{element} (100B) + +V4H mechs/marauder/armaturedata/joint_cage.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_hip.data (12B) + +V4H mechs/marauder/armaturedata/mar_hip.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_hip.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_lbtoe.data (12B) + +V4H mechs/marauder/armaturedata/mar_lbtoe.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_lbtoe.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_ldleg.data (12B) + +V4H mechs/marauder/armaturedata/mar_ldleg.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_ldleg.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_lfoot.data (12B) + +V4H mechs/marauder/armaturedata/mar_lfoot.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_lfoot.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_lgun.data (12B) + +V4H mechs/marauder/armaturedata/mar_lgun.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_lgun.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_litoe.data (12B) + +V4H mechs/marauder/armaturedata/mar_litoe.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_litoe.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_lotoe.data (12B) + +V4H mechs/marauder/armaturedata/mar_lotoe.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_lotoe.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_luarm.data (12B) + +V4H mechs/marauder/armaturedata/mar_luarm.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_luarm.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_luleg.data (12B) + +V4H mechs/marauder/armaturedata/mar_luleg.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_luleg.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_rbtoe.data (12B) + +V4H mechs/marauder/armaturedata/mar_rbtoe.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_rbtoe.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_rdleg.data (12B) + +V4H mechs/marauder/armaturedata/mar_rdleg.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_rdleg.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_rfoot.data (12B) + +V4H mechs/marauder/armaturedata/mar_rfoot.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_rfoot.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_rgun.data (12B) + +V4H mechs/marauder/armaturedata/mar_rgun.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_rgun.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_ritoe.data (12B) + +V4H mechs/marauder/armaturedata/mar_ritoe.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_ritoe.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_rotoe.data (12B) + +V4H mechs/marauder/armaturedata/mar_rotoe.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_rotoe.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_ruarm.data (12B) + +V4H mechs/marauder/armaturedata/mar_ruarm.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_ruarm.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_ruleg.data (12B) + +V4H mechs/marauder/armaturedata/mar_ruleg.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_ruleg.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_specialone.data (12B) + +V4H mechs/marauder/armaturedata/mar_specialone.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_specialone.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_specialtwo.data (12B) + +V4H mechs/marauder/armaturedata/mar_specialtwo.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_specialtwo.data{gamemodel} (80B) + +V4H mechs/marauder/armaturedata/mar_torso.data (12B) + +V4H mechs/marauder/armaturedata/mar_torso.data{element} (100B) + +V4H mechs/marauder/armaturedata/mar_torso.data{gamemodel} (80B) + +V4H mechs/marauder/armaturevideo/joint_cage.video (646B) + +V4H mechs/marauder/armaturevideo/mar_hip.video (406B) + +V4H mechs/marauder/armaturevideo/mar_lbtoe.video (285B) + +V4H mechs/marauder/armaturevideo/mar_ldleg.video (406B) + +V4H mechs/marauder/armaturevideo/mar_lfoot.video (406B) + +V4H mechs/marauder/armaturevideo/mar_lgun.video (289B) + +V4H mechs/marauder/armaturevideo/mar_litoe.video (406B) + +V4H mechs/marauder/armaturevideo/mar_lotoe.video (285B) + +V4H mechs/marauder/armaturevideo/mar_luarm.video (406B) + +V4H mechs/marauder/armaturevideo/mar_luleg.video (406B) + +V4H mechs/marauder/armaturevideo/mar_rbtoe.video (285B) + +V4H mechs/marauder/armaturevideo/mar_rdleg.video (406B) + +V4H mechs/marauder/armaturevideo/mar_rfoot.video (406B) + +V4H mechs/marauder/armaturevideo/mar_rgun.video (285B) + +V4H mechs/marauder/armaturevideo/mar_ritoe.video (406B) + +V4H mechs/marauder/armaturevideo/mar_rotoe.video (406B) + +V4H mechs/marauder/armaturevideo/mar_ruarm.video (406B) + +V4H mechs/marauder/armaturevideo/mar_ruleg.video (406B) + +V4H mechs/marauder/armaturevideo/mar_specialone.video (406B) + +V4H mechs/marauder/armaturevideo/mar_specialtwo.video (406B) + +V4H mechs/marauder/armaturevideo/mar_torso.video (683B) + +V4H mechs/marauder/marauder.contents (282B) + +V4H mechs/marauder/marauder.contents[joint_cage]{sites} (124B) + +V4H mechs/marauder/marauder.contents[joint_hip]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_hipabove]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_hipbelow]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_lankle]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_lbelowankle]{armature} (1122B) + +V4H mechs/marauder/marauder.contents[joint_lbelowankle]{sites} (39B) + +V4H mechs/marauder/marauder.contents[joint_ldleg]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_lefttorsofront]{sites} (44B) + +V4H mechs/marauder/marauder.contents[joint_lgun]{sites} (130B) + +V4H mechs/marauder/marauder.contents[joint_lgunabove]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_luarm]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_luleg]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_rankle]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_rbelowankle]{armature} (1122B) + +V4H mechs/marauder/marauder.contents[joint_rbelowankle]{sites} (39B) + +V4H mechs/marauder/marauder.contents[joint_rdleg]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_rgun]{sites} (130B) + +V4H mechs/marauder/marauder.contents[joint_rgunabove]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_righttorsofront]{sites} (44B) + +V4H mechs/marauder/marauder.contents[joint_root]{armature} (842B) + +V4H mechs/marauder/marauder.contents[joint_ruarm]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_ruleg]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_specialone]{sites} (42B) + +V4H mechs/marauder/marauder.contents[joint_specialtwo]{sites} (42B) + +V4H mechs/marauder/marauder.contents[joint_torso]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_torsoabove]{armature} (3082B) + +V4H mechs/marauder/marauder.contents[joint_torsoabove]{sites} (477B) + +V4H mechs/marauder/marauder.contents[joint_torsobelow]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_vel]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_world]{armature} (282B) + +V4H mechs/marauder/marauder.contents[joint_world]{sites} (40B) + +V4H mechs/marauder/marauder.damage (1688B) + +V4H mechs/marauder/marauder.data (12B) + +V4H mechs/marauder/marauder.data[shadow] (117B) + +V4H mechs/marauder/marauder.data{element} (100B) + +V4H mechs/marauder/marauder.data{footsteps} (102B) + +V4H mechs/marauder/marauder.data{gamemodel} (1636B) + +V4H mechs/marauder/marauder.data{hierarchicalobb} (7322B) + +V4H mechs/marauder/marauder.data{solidobb} (78B) + +V4H mechs/marauder/marauder.engine (12B) + +V4H mechs/marauder/marauder.engine{element} (100B) + +V4H mechs/marauder/marauder.engine{gamemodel} (64B) + +V4H mechs/marauder/marauder.instance (344B) + +V4H mechs/marauder/marauder.subsystems (4002B) + +V4H mechs/marauder/marauder.torso (12B) + +V4H mechs/marauder/marauder.torso{element} (100B) + +V4H mechs/marauder/marauder.torso{gamemodel} (1608B) + +V4H mechs/marauder_destroyed/marauder_destroyed.data (12B) + +V4H mechs/marauder_destroyed/marauder_destroyed.data{element} (100B) + +V4H mechs/marauder_destroyed/marauder_destroyed.data{gamemodel} (32B) + +V4H mechs/marauder_destroyed/marauder_destroyed.video (285B) + +V4H mechs/marauder_destroyed/marauder_destroyed_solid.obb (78B) + +V4H mechs/thunderbolt/armaturedata/joint_cage.data (12B) + +V4H mechs/thunderbolt/armaturedata/joint_cage.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/joint_cage.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_hip.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_hip.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_hip.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_lbtoe.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_lbtoe.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_lbtoe.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_ldleg.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_ldleg.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_ldleg.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_lfoot.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_lfoot.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_lfoot.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_lftoe.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_lftoe.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_lftoe.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_lgun.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_lgun.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_lgun.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_luarm.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_luarm.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_luarm.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_luleg.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_luleg.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_luleg.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_rbtoe.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_rbtoe.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_rbtoe.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_rdleg.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_rdleg.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_rdleg.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_rfoot.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_rfoot.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_rfoot.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_rftoe.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_rftoe.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_rftoe.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_rgun.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_rgun.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_rgun.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_ruarm.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_ruarm.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_ruarm.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_ruleg.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_ruleg.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_ruleg.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_specialone.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_specialone.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_specialone.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturedata/thu_torso.data (12B) + +V4H mechs/thunderbolt/armaturedata/thu_torso.data{element} (100B) + +V4H mechs/thunderbolt/armaturedata/thu_torso.data{gamemodel} (80B) + +V4H mechs/thunderbolt/armaturevideo/joint_cage.video (646B) + +V4H mechs/thunderbolt/armaturevideo/thu_hip.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_lbtoe.video (285B) + +V4H mechs/thunderbolt/armaturevideo/thu_ldleg.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_lfoot.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_lftoe.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_lgun.video (285B) + +V4H mechs/thunderbolt/armaturevideo/thu_luarm.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_luleg.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_rbtoe.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_rdleg.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_rfoot.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_rftoe.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_rgun.video (285B) + +V4H mechs/thunderbolt/armaturevideo/thu_ruarm.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_ruleg.video (406B) + +V4H mechs/thunderbolt/armaturevideo/thu_specialone.video (285B) + +V4H mechs/thunderbolt/armaturevideo/thu_torso.video (683B) + +V4H mechs/thunderbolt/thunderbolt.contents (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_cage]{sites} (124B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_hip]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_hipabove]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_hipbelow]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_lankle]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_lbelowankle]{armature} (842B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_lbelowankle]{sites} (39B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_ldleg]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_lefttorsofront]{sites} (44B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_lgun]{sites} (130B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_lgunabove]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_luarm]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_luleg]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_rankle]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_rbelowankle]{armature} (842B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_rbelowankle]{sites} (39B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_rdleg]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_rgun]{sites} (130B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_rgunabove]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_righttorsofront]{sites} (44B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_root]{armature} (842B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_ruarm]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_ruleg]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_specialone]{sites} (86B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_torso]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_torsoabove]{armature} (2802B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_torsoabove]{sites} (393B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_torsobelow]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_vel]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_world]{armature} (282B) + +V4H mechs/thunderbolt/thunderbolt.contents[joint_world]{sites} (40B) + +V4H mechs/thunderbolt/thunderbolt.damage (1522B) + +V4H mechs/thunderbolt/thunderbolt.data (12B) + +V4H mechs/thunderbolt/thunderbolt.data[shadow] (117B) + +V4H mechs/thunderbolt/thunderbolt.data{element} (100B) + +V4H mechs/thunderbolt/thunderbolt.data{footsteps} (110B) + +V4H mechs/thunderbolt/thunderbolt.data{gamemodel} (1636B) + +V4H mechs/thunderbolt/thunderbolt.data{hierarchicalobb} (5800B) + +V4H mechs/thunderbolt/thunderbolt.data{solidobb} (78B) + +V4H mechs/thunderbolt/thunderbolt.engine (12B) + +V4H mechs/thunderbolt/thunderbolt.engine{element} (100B) + +V4H mechs/thunderbolt/thunderbolt.engine{gamemodel} (64B) + +V4H mechs/thunderbolt/thunderbolt.instance (344B) + +V4H mechs/thunderbolt/thunderbolt.subsystems (5598B) + +V4H mechs/thunderbolt/thunderbolt.torso (12B) + +V4H mechs/thunderbolt/thunderbolt.torso{element} (100B) + +V4H mechs/thunderbolt/thunderbolt.torso{gamemodel} (1608B) + +V4H mechs/thunderbolt_destroyed/thunderbolt_destroyed.data (12B) + +V4H mechs/thunderbolt_destroyed/thunderbolt_destroyed.data{element} (100B) + +V4H mechs/thunderbolt_destroyed/thunderbolt_destroyed.data{gamemodel} (32B) + +V4H mechs/thunderbolt_destroyed/thunderbolt_destroyed.obb (78B) + +V4H mechs/thunderbolt_destroyed/thunderbolt_destroyed.video (285B) +== maps/alpine02.mw4: V4H=458 OURS=458 | names: common=458 V4H_only=0 OURS_only=0 | blobs: identical=313 V4H_uniq=145 OURS_uniq=145 +== maps/arctic04.mw4: V4H=341 OURS=341 | names: common=341 V4H_only=0 OURS_only=0 | blobs: identical=306 V4H_uniq=35 OURS_uniq=35 +== maps/arctic06.mw4: V4H=342 OURS=342 | names: common=342 V4H_only=0 OURS_only=0 | blobs: identical=305 V4H_uniq=37 OURS_uniq=37 +== maps/colsm01.mw4: V4H=182 OURS=182 | names: common=182 V4H_only=0 OURS_only=0 | blobs: identical=152 V4H_uniq=30 OURS_uniq=30 +== maps/colsm01_backup.mw4: OURS-ONLY PACKAGE (1 entries) +== maps/colsm02.mw4: V4H=248 OURS=248 | names: common=248 V4H_only=0 OURS_only=0 | blobs: identical=193 V4H_uniq=55 OURS_uniq=55 +== maps/conroe01.mw4: OURS-ONLY PACKAGE (64 entries) +== maps/conroe02.mw4: OURS-ONLY PACKAGE (72 entries) +== maps/conroe03.mw4: OURS-ONLY PACKAGE (64 entries) +== maps/darklord.mw4: V4H=135 OURS=135 | names: common=135 V4H_only=0 OURS_only=0 | blobs: identical=113 V4H_uniq=22 OURS_uniq=22 +== maps/ddc_msl.mw4: OURS-ONLY PACKAGE (134 entries) +== maps/desert.mw4: OURS-ONLY PACKAGE (219 entries) +== maps/desert07.mw4: V4H=399 OURS=399 | names: common=399 V4H_only=0 OURS_only=0 | blobs: identical=331 V4H_uniq=68 OURS_uniq=68 +== maps/doneg01.mw4: OURS-ONLY PACKAGE (413 entries) +== maps/fact01.mw4: V4H=201 OURS=201 | names: common=201 V4H_only=0 OURS_only=0 | blobs: identical=161 V4H_uniq=40 OURS_uniq=40 +== maps/firestorm.mw4: V4H=63 OURS=63 | names: common=63 V4H_only=0 OURS_only=0 | blobs: identical=60 V4H_uniq=3 OURS_uniq=3 +== maps/freezer.mw4: OURS-ONLY PACKAGE (219 entries) +== maps/gage.mw4: OURS-ONLY PACKAGE (406 entries) +== maps/grassland.mw4: V4H=287 OURS=287 | names: common=287 V4H_only=0 OURS_only=0 | blobs: identical=269 V4H_uniq=18 OURS_uniq=18 +== maps/hotplate.mw4: V4H=219 OURS=219 | names: common=219 V4H_only=0 OURS_only=0 | blobs: identical=200 V4H_uniq=19 OURS_uniq=19 +== maps/ice3.mw4: V4H=219 OURS=219 | names: common=219 V4H_only=0 OURS_only=0 | blobs: identical=191 V4H_uniq=28 OURS_uniq=28 +== maps/jung02.mw4: V4H=174 OURS=174 | names: common=174 V4H_only=0 OURS_only=0 | blobs: identical=146 V4H_uniq=28 OURS_uniq=28 +== maps/lunar01.mw4: V4H=249 OURS=249 | names: common=249 V4H_only=0 OURS_only=0 | blobs: identical=215 V4H_uniq=34 OURS_uniq=34 +== maps/minerl01.mw4: OURS-ONLY PACKAGE (361 entries) +== maps/minerl03.mw4: V4H=358 OURS=358 | names: common=358 V4H_only=0 OURS_only=0 | blobs: identical=305 V4H_uniq=53 OURS_uniq=53 +== maps/mountn01.mw4: OURS-ONLY PACKAGE (418 entries) +== maps/mountn03.mw4: OURS-ONLY PACKAGE (416 entries) +== maps/nazca.mw4: V4H=224 OURS=224 | names: common=224 V4H_only=0 OURS_only=0 | blobs: identical=196 V4H_uniq=28 OURS_uniq=28 +== maps/ngoth.mw4: OURS-ONLY PACKAGE (314 entries) +== maps/palace01.mw4: V4H=244 OURS=244 | names: common=244 V4H_only=0 OURS_only=0 | blobs: identical=230 V4H_uniq=14 OURS_uniq=14 +== maps/peaks.mw4: V4H=219 OURS=219 | names: common=219 V4H_only=0 OURS_only=0 | blobs: identical=210 V4H_uniq=9 OURS_uniq=9 +== maps/reduex.mw4: V4H=248 OURS=248 | names: common=248 V4H_only=0 OURS_only=0 | blobs: identical=213 V4H_uniq=35 OURS_uniq=35 +== maps/rookiearena2-terrain-v1.mw4: V4H=63 OURS=63 | names: common=63 V4H_only=0 OURS_only=0 | blobs: identical=52 V4H_uniq=11 OURS_uniq=11 +== maps/ruin03.mw4: OURS-ONLY PACKAGE (390 entries) +== maps/scrub01.mw4: OURS-ONLY PACKAGE (439 entries) +== maps/scrub02.mw4: V4H=355 OURS=355 | names: common=355 V4H_only=0 OURS_only=0 | blobs: identical=309 V4H_uniq=46 OURS_uniq=46 +== maps/scrub06.mw4: OURS-ONLY PACKAGE (249 entries) +== maps/stormcanyon.mw4: V4H=128 OURS=128 | names: common=128 V4H_only=0 OURS_only=0 | blobs: identical=109 V4H_uniq=19 OURS_uniq=19 +== maps/stormcanyonsiege.mw4: V4H=137 OURS=137 | names: common=137 V4H_only=0 OURS_only=0 | blobs: identical=120 V4H_uniq=17 OURS_uniq=17 +== maps/stormcanyonsiege_backup.mw4: OURS-ONLY PACKAGE (1 entries) +== maps/swamp01.mw4: V4H=488 OURS=488 | names: common=488 V4H_only=0 OURS_only=0 | blobs: identical=343 V4H_uniq=145 OURS_uniq=145 +== maps/urban01.mw4: V4H=446 OURS=446 | names: common=446 V4H_only=0 OURS_only=0 | blobs: identical=363 V4H_uniq=83 OURS_uniq=83 +== maps/urban02.mw4: V4H=396 OURS=396 | names: common=396 V4H_only=0 OURS_only=0 | blobs: identical=337 V4H_uniq=59 OURS_uniq=59 +== maps/urban05.mw4: V4H=396 OURS=396 | names: common=396 V4H_only=0 OURS_only=0 | blobs: identical=346 V4H_uniq=50 OURS_uniq=50 +== maps/volcan01.mw4: OURS-ONLY PACKAGE (256 entries) +== maps/volcan03.mw4: OURS-ONLY PACKAGE (197 entries) +== missions/aspen.mw4: OURS-ONLY PACKAGE (134 entries) +== missions/bigcity.mw4: V4H=80 OURS=80 | names: common=80 V4H_only=0 OURS_only=0 | blobs: identical=72 V4H_uniq=8 OURS_uniq=8 +== missions/cantina.mw4: V4H=31 OURS=31 | names: common=31 V4H_only=0 OURS_only=0 | blobs: identical=25 V4H_uniq=6 OURS_uniq=6 +== missions/cantinasiege.mw4: V4H=50 OURS=50 | names: common=50 V4H_only=0 OURS_only=0 | blobs: identical=37 V4H_uniq=13 OURS_uniq=13 +== missions/canyon.mw4: OURS-ONLY PACKAGE (28 entries) +== missions/coliseum.mw4: V4H=47 OURS=47 | names: common=47 V4H_only=0 OURS_only=0 | blobs: identical=36 V4H_uniq=11 OURS_uniq=11 +== missions/coliseum_backup.mw4: OURS-ONLY PACKAGE (1 entries) +== missions/conroe01.mw4: OURS-ONLY PACKAGE (117 entries) +== missions/conroe02.mw4: OURS-ONLY PACKAGE (119 entries) +== missions/conroe03.mw4: OURS-ONLY PACKAGE (119 entries) +== missions/cpark.mw4: V4H=31 OURS=31 | names: common=31 V4H_only=0 OURS_only=0 | blobs: identical=26 V4H_uniq=5 OURS_uniq=5 +== missions/dustbowl.mw4: V4H=28 OURS=28 | names: common=28 V4H_only=0 OURS_only=0 | blobs: identical=23 V4H_uniq=5 OURS_uniq=5 +== missions/editortemplate.mw4: OURS-ONLY PACKAGE (116 entries) +== missions/factory.mw4: V4H=25 OURS=25 | names: common=25 V4H_only=0 OURS_only=0 | blobs: identical=16 V4H_uniq=9 OURS_uniq=9 +== missions/fbite.mw4: V4H=32 OURS=32 | names: common=32 V4H_only=0 OURS_only=0 | blobs: identical=25 V4H_uniq=7 OURS_uniq=7 +== missions/freezer.mw4: V4H=165 OURS=135 | names: common=135 V4H_only=30 OURS_only=0 | blobs: identical=119 V4H_uniq=46 OURS_uniq=16 + +V4H audio/vo/generic/mp/ahh.wav (54966B) + +V4H audio/vo/generic/mp/ahh.wav{handle} (10B) + +V4H audio/vo/generic/mp/aww.wav (11958B) + +V4H audio/vo/generic/mp/aww.wav{handle} (10B) + +V4H audio/vo/generic/mp/boo1.wav (97718B) + +V4H audio/vo/generic/mp/boo1.wav{handle} (10B) + +V4H audio/vo/generic/mp/boo2.wav (131510B) + +V4H audio/vo/generic/mp/boo2.wav{handle} (10B) + +V4H audio/vo/generic/mp/boo3.wav (138166B) + +V4H audio/vo/generic/mp/boo3.wav{handle} (10B) + +V4H audio/vo/generic/mp/cheer1.wav (200118B) + +V4H audio/vo/generic/mp/cheer1.wav{handle} (10B) + +V4H audio/vo/generic/mp/cheer2.wav (102838B) + +V4H audio/vo/generic/mp/cheer2.wav{handle} (10B) + +V4H audio/vo/generic/mp/cheer3.wav (95670B) + +V4H audio/vo/generic/mp/cheer3.wav{handle} (10B) + +V4H audio/vo/generic/mp/cheer4.wav (152246B) + +V4H audio/vo/generic/mp/cheer4.wav{handle} (10B) + +V4H audio/vo/generic/mp/cheer5.wav (92086B) + +V4H audio/vo/generic/mp/cheer5.wav{handle} (10B) + +V4H audio/vo/generic/mp/cheer8.wav (14076B) + +V4H audio/vo/generic/mp/cheer8.wav{handle} (10B) + +V4H audio/vo/generic/mp/cheer9.wav (63670B) + +V4H audio/vo/generic/mp/cheer9.wav{handle} (10B) + +V4H audio/vo/generic/mp/jeers.wav (127414B) + +V4H audio/vo/generic/mp/jeers.wav{handle} (10B) + +V4H audio/vo/generic/mp/roar.wav (9654B) + +V4H audio/vo/generic/mp/roar.wav{handle} (10B) + +V4H audio/vo/generic/mp/stadamb.wav (23478B) + +V4H audio/vo/generic/mp/stdamb.wav{handle} (10B) +== missions/gagetown.mw4: OURS-ONLY PACKAGE (30 entries) +== missions/gbait.mw4: V4H=32 OURS=32 | names: common=32 V4H_only=0 OURS_only=0 | blobs: identical=25 V4H_uniq=7 OURS_uniq=7 +== missions/ghosthighway.mw4: V4H=30 OURS=30 | names: common=30 V4H_only=0 OURS_only=0 | blobs: identical=23 V4H_uniq=7 OURS_uniq=7 +== missions/gladiatorpit.mw4: OURS-ONLY PACKAGE (26 entries) +== missions/grassland.mw4: V4H=133 OURS=133 | names: common=133 V4H_only=0 OURS_only=0 | blobs: identical=113 V4H_uniq=20 OURS_uniq=20 +== missions/hideaway.mw4: V4H=131 OURS=131 | names: common=131 V4H_only=0 OURS_only=0 | blobs: identical=112 V4H_uniq=19 OURS_uniq=19 +== missions/hotplate.mw4: V4H=129 OURS=129 | names: common=129 V4H_only=0 OURS_only=0 | blobs: identical=115 V4H_uniq=14 OURS_uniq=14 +== missions/icity.mw4: V4H=26 OURS=26 | names: common=26 V4H_only=0 OURS_only=0 | blobs: identical=20 V4H_uniq=6 OURS_uniq=6 +== missions/jungle.mw4: V4H=36 OURS=36 | names: common=36 V4H_only=0 OURS_only=0 | blobs: identical=29 V4H_uniq=7 OURS_uniq=7 +== missions/lakeside.mw4: OURS-ONLY PACKAGE (30 entries) +== missions/lunacy.mw4: V4H=25 OURS=25 | names: common=25 V4H_only=0 OURS_only=0 | blobs: identical=18 V4H_uniq=7 OURS_uniq=7 +== missions/mechworks.mw4: OURS-ONLY PACKAGE (24 entries) +== missions/minehq.mw4: OURS-ONLY PACKAGE (24 entries) +== missions/nazca.mw4: V4H=134 OURS=134 | names: common=134 V4H_only=0 OURS_only=0 | blobs: identical=110 V4H_uniq=24 OURS_uniq=24 +== missions/newgothem.mw4: OURS-ONLY PACKAGE (25 entries) +== missions/peaks.mw4: V4H=129 OURS=129 | names: common=129 V4H_only=0 OURS_only=0 | blobs: identical=118 V4H_uniq=11 OURS_uniq=11 +== missions/pgates.mw4: V4H=155 OURS=155 | names: common=155 V4H_only=0 OURS_only=0 | blobs: identical=141 V4H_uniq=14 OURS_uniq=14 +== missions/reduex.mw4: V4H=128 OURS=128 | names: common=128 V4H_only=0 OURS_only=0 | blobs: identical=106 V4H_uniq=22 OURS_uniq=22 +== missions/reduexsiege.mw4: V4H=137 OURS=137 | names: common=137 V4H_only=0 OURS_only=0 | blobs: identical=115 V4H_uniq=22 OURS_uniq=22 +== missions/rubble.mw4: OURS-ONLY PACKAGE (66 entries) +== missions/sanddunes.mw4: OURS-ONLY PACKAGE (119 entries) +== missions/scarabstronghold.mw4: V4H=123 OURS=123 | names: common=123 V4H_only=0 OURS_only=0 | blobs: identical=107 V4H_uniq=16 OURS_uniq=16 +== missions/snowjob.mw4: V4H=29 OURS=29 | names: common=29 V4H_only=0 OURS_only=0 | blobs: identical=22 V4H_uniq=7 OURS_uniq=7 +== missions/spaceport.mw4: OURS-ONLY PACKAGE (67 entries) +== missions/stormcanyon.mw4: V4H=181 OURS=181 | names: common=181 V4H_only=0 OURS_only=0 | blobs: identical=162 V4H_uniq=19 OURS_uniq=19 +== missions/stormcanyonsiege.mw4: V4H=163 OURS=163 | names: common=163 V4H_only=0 OURS_only=0 | blobs: identical=136 V4H_uniq=27 OURS_uniq=27 +== missions/tline.mw4: V4H=31 OURS=31 | names: common=31 V4H_only=0 OURS_only=0 | blobs: identical=26 V4H_uniq=5 OURS_uniq=5 +== missions/tribeincursion.mw4: V4H=71 OURS=71 | names: common=71 V4H_only=0 OURS_only=0 | blobs: identical=61 V4H_uniq=10 OURS_uniq=10 +== missions/tribeincursionmission.mw4: V4H=79 OURS=79 | names: common=79 V4H_only=0 OURS_only=0 | blobs: identical=66 V4H_uniq=13 OURS_uniq=13 +== missions/vbase.mw4: OURS-ONLY PACKAGE (28 entries) +== pilots/tesla/options.mw4: V4H=2 OURS=2 | names: common=2 V4H_only=0 OURS_only=0 | blobs: identical=0 V4H_uniq=2 OURS_uniq=2 +== props.mw4: V4H=10779 OURS=10627 | names: common=10624 V4H_only=155 OURS_only=3 | blobs: identical=4533 V4H_uniq=6246 OURS_uniq=6094 + +V4H mechs/jenner2c/animation/j2c_back.mw4anim (8219B) + +V4H mechs/jenner2c/animation/j2c_backd.mw4anim (8021B) + +V4H mechs/jenner2c/animation/j2c_backl.mw4anim (8001B) + +V4H mechs/jenner2c/animation/j2c_backr.mw4anim (8561B) + +V4H mechs/jenner2c/animation/j2c_backstand.mw4anim (4491B) + +V4H mechs/jenner2c/animation/j2c_backstandd.mw4anim (4605B) + +V4H mechs/jenner2c/animation/j2c_backstandl.mw4anim (4389B) + +V4H mechs/jenner2c/animation/j2c_backstandr.mw4anim (4249B) + +V4H mechs/jenner2c/animation/j2c_backstandrev.mw4anim (4491B) + +V4H mechs/jenner2c/animation/j2c_backstandrevd.mw4anim (4605B) + +V4H mechs/jenner2c/animation/j2c_backstandrevl.mw4anim (4249B) + +V4H mechs/jenner2c/animation/j2c_backstandrevr.mw4anim (4389B) + +V4H mechs/jenner2c/animation/j2c_backstandrevu.mw4anim (4665B) + +V4H mechs/jenner2c/animation/j2c_backstandu.mw4anim (4665B) + +V4H mechs/jenner2c/animation/j2c_backu.mw4anim (8665B) + +V4H mechs/jenner2c/animation/j2c_fallback.mw4anim (18365B) + +V4H mechs/jenner2c/animation/j2c_fallforward.mw4anim (17909B) + +V4H mechs/jenner2c/animation/j2c_fallleft.mw4anim (18445B) + +V4H mechs/jenner2c/animation/j2c_fallpose.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_fallright.mw4anim (18445B) + +V4H mechs/jenner2c/animation/j2c_getup.mw4anim (13201B) + +V4H mechs/jenner2c/animation/j2c_getupright.mw4anim (13201B) + +V4H mechs/jenner2c/animation/j2c_jump.mw4anim (4181B) + +V4H mechs/jenner2c/animation/j2c_landforward.mw4anim (8067B) + +V4H mechs/jenner2c/animation/j2c_landstand.mw4anim (6143B) + +V4H mechs/jenner2c/animation/j2c_landstandd.mw4anim (6223B) + +V4H mechs/jenner2c/animation/j2c_landstandl.mw4anim (6443B) + +V4H mechs/jenner2c/animation/j2c_landstandr.mw4anim (6443B) + +V4H mechs/jenner2c/animation/j2c_landstandu.mw4anim (6163B) + +V4H mechs/jenner2c/animation/j2c_lgimp.mw4anim (7991B) + +V4H mechs/jenner2c/animation/j2c_lgimpd.mw4anim (8009B) + +V4H mechs/jenner2c/animation/j2c_lgimpl.mw4anim (7769B) + +V4H mechs/jenner2c/animation/j2c_lgimppose.mw4anim (1543B) + +V4H mechs/jenner2c/animation/j2c_lgimpposed.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_lgimpposel.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_lgimpposer.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_lgimpposeu.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_lgimpr.mw4anim (7969B) + +V4H mechs/jenner2c/animation/j2c_lgimpstand.mw4anim (4247B) + +V4H mechs/jenner2c/animation/j2c_lgimpstandd.mw4anim (4277B) + +V4H mechs/jenner2c/animation/j2c_lgimpstandl.mw4anim (4001B) + +V4H mechs/jenner2c/animation/j2c_lgimpstandr.mw4anim (4213B) + +V4H mechs/jenner2c/animation/j2c_lgimpstandu.mw4anim (4237B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnleft.mw4anim (4423B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnleftd.mw4anim (4241B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnleftl.mw4anim (4321B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnleftr.mw4anim (4241B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnleftu.mw4anim (4241B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnright.mw4anim (3895B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnrightd.mw4anim (3789B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnrightl.mw4anim (3809B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnrightr.mw4anim (3749B) + +V4H mechs/jenner2c/animation/j2c_lgimpturnrightu.mw4anim (3689B) + +V4H mechs/jenner2c/animation/j2c_lgimpu.mw4anim (7989B) + +V4H mechs/jenner2c/animation/j2c_powerdown.mw4anim (3025B) + +V4H mechs/jenner2c/animation/j2c_powerdownd.mw4anim (3025B) + +V4H mechs/jenner2c/animation/j2c_powerdownl.mw4anim (3145B) + +V4H mechs/jenner2c/animation/j2c_powerdownr.mw4anim (3145B) + +V4H mechs/jenner2c/animation/j2c_powerdownu.mw4anim (3025B) + +V4H mechs/jenner2c/animation/j2c_powerup.mw4anim (3025B) + +V4H mechs/jenner2c/animation/j2c_powerupd.mw4anim (3045B) + +V4H mechs/jenner2c/animation/j2c_powerupl.mw4anim (3165B) + +V4H mechs/jenner2c/animation/j2c_powerupr.mw4anim (3165B) + +V4H mechs/jenner2c/animation/j2c_powerupu.mw4anim (3025B) + +V4H mechs/jenner2c/animation/j2c_rgimp.mw4anim (7991B) + +V4H mechs/jenner2c/animation/j2c_rgimpd.mw4anim (8009B) + +V4H mechs/jenner2c/animation/j2c_rgimpl.mw4anim (7969B) + +V4H mechs/jenner2c/animation/j2c_rgimppose.mw4anim (1543B) + +V4H mechs/jenner2c/animation/j2c_rgimpposed.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_rgimpposel.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_rgimpposer.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_rgimpposeu.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_rgimpr.mw4anim (7769B) + +V4H mechs/jenner2c/animation/j2c_rgimpstand.mw4anim (4227B) + +V4H mechs/jenner2c/animation/j2c_rgimpstandd.mw4anim (4277B) + +V4H mechs/jenner2c/animation/j2c_rgimpstandl.mw4anim (4213B) + +V4H mechs/jenner2c/animation/j2c_rgimpstandr.mw4anim (4001B) + +V4H mechs/jenner2c/animation/j2c_rgimpstandu.mw4anim (4237B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnleft.mw4anim (3895B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnleftd.mw4anim (3789B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnleftl.mw4anim (3749B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnleftr.mw4anim (3809B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnleftu.mw4anim (3689B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnright.mw4anim (4423B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnrightd.mw4anim (4241B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnrightl.mw4anim (4241B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnrightr.mw4anim (4321B) + +V4H mechs/jenner2c/animation/j2c_rgimpturnrightu.mw4anim (4241B) + +V4H mechs/jenner2c/animation/j2c_rgimpu.mw4anim (7989B) + +V4H mechs/jenner2c/animation/j2c_run.mw4anim (8443B) + +V4H mechs/jenner2c/animation/j2c_rund.mw4anim (7705B) + +V4H mechs/jenner2c/animation/j2c_runl.mw4anim (8089B) + +V4H mechs/jenner2c/animation/j2c_runr.mw4anim (7849B) + +V4H mechs/jenner2c/animation/j2c_runu.mw4anim (8209B) + +V4H mechs/jenner2c/animation/j2c_squatdown.mw4anim (6813B) + +V4H mechs/jenner2c/animation/j2c_squatdownd.mw4anim (6713B) + +V4H mechs/jenner2c/animation/j2c_squatdownl.mw4anim (7293B) + +V4H mechs/jenner2c/animation/j2c_squatdownr.mw4anim (6853B) + +V4H mechs/jenner2c/animation/j2c_squatdownu.mw4anim (7153B) + +V4H mechs/jenner2c/animation/j2c_squatup.mw4anim (6733B) + +V4H mechs/jenner2c/animation/j2c_squatupd.mw4anim (6653B) + +V4H mechs/jenner2c/animation/j2c_squatupl.mw4anim (7173B) + +V4H mechs/jenner2c/animation/j2c_squatupr.mw4anim (6733B) + +V4H mechs/jenner2c/animation/j2c_squatupu.mw4anim (7033B) + +V4H mechs/jenner2c/animation/j2c_standback.mw4anim (4731B) + +V4H mechs/jenner2c/animation/j2c_standbackd.mw4anim (4877B) + +V4H mechs/jenner2c/animation/j2c_standbackl.mw4anim (4669B) + +V4H mechs/jenner2c/animation/j2c_standbackr.mw4anim (4617B) + +V4H mechs/jenner2c/animation/j2c_standbacku.mw4anim (4557B) + +V4H mechs/jenner2c/animation/j2c_standlgimp.mw4anim (5007B) + +V4H mechs/jenner2c/animation/j2c_standlgimpd.mw4anim (4733B) + +V4H mechs/jenner2c/animation/j2c_standlgimpl.mw4anim (4745B) + +V4H mechs/jenner2c/animation/j2c_standlgimpr.mw4anim (4861B) + +V4H mechs/jenner2c/animation/j2c_standlgimpu.mw4anim (4909B) + +V4H mechs/jenner2c/animation/j2c_standpose.mw4anim (1543B) + +V4H mechs/jenner2c/animation/j2c_standposed.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_standposel.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_standposer.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_standposeu.mw4anim (1377B) + +V4H mechs/jenner2c/animation/j2c_standrgimp.mw4anim (5007B) + +V4H mechs/jenner2c/animation/j2c_standrgimpd.mw4anim (4733B) + +V4H mechs/jenner2c/animation/j2c_standrgimpl.mw4anim (4861B) + +V4H mechs/jenner2c/animation/j2c_standrgimpr.mw4anim (4745B) + +V4H mechs/jenner2c/animation/j2c_standrgimpu.mw4anim (4909B) + +V4H mechs/jenner2c/animation/j2c_standwalk.mw4anim (4491B) + +V4H mechs/jenner2c/animation/j2c_standwalkd.mw4anim (4609B) + +V4H mechs/jenner2c/animation/j2c_standwalkl.mw4anim (4369B) + +V4H mechs/jenner2c/animation/j2c_standwalkr.mw4anim (4229B) + +V4H mechs/jenner2c/animation/j2c_standwalku.mw4anim (4605B) + +V4H mechs/jenner2c/animation/j2c_turnleft.mw4anim (3795B) + +V4H mechs/jenner2c/animation/j2c_turnleftd.mw4anim (3533B) + +V4H mechs/jenner2c/animation/j2c_turnleftl.mw4anim (3733B) + +V4H mechs/jenner2c/animation/j2c_turnleftr.mw4anim (3733B) + +V4H mechs/jenner2c/animation/j2c_turnleftu.mw4anim (3693B) + +V4H mechs/jenner2c/animation/j2c_turnright.mw4anim (3795B) + +V4H mechs/jenner2c/animation/j2c_turnrightd.mw4anim (3533B) + +V4H mechs/jenner2c/animation/j2c_turnrightl.mw4anim (3733B) + +V4H mechs/jenner2c/animation/j2c_turnrightr.mw4anim (3733B) + +V4H mechs/jenner2c/animation/j2c_turnrightu.mw4anim (3693B) + +V4H mechs/jenner2c/animation/j2c_walk.mw4anim (8783B) + +V4H mechs/jenner2c/animation/j2c_walkd.mw4anim (8693B) + +V4H mechs/jenner2c/animation/j2c_walkl.mw4anim (8589B) + +V4H mechs/jenner2c/animation/j2c_walkr.mw4anim (8585B) + +V4H mechs/jenner2c/animation/j2c_walkstand.mw4anim (4847B) + +V4H mechs/jenner2c/animation/j2c_walkstandd.mw4anim (4597B) + +V4H mechs/jenner2c/animation/j2c_walkstandl.mw4anim (4573B) + +V4H mechs/jenner2c/animation/j2c_walkstandr.mw4anim (4557B) + +V4H mechs/jenner2c/animation/j2c_walkstandrev.mw4anim (4675B) + +V4H mechs/jenner2c/animation/j2c_walkstandrevd.mw4anim (4597B) + +V4H mechs/jenner2c/animation/j2c_walkstandrevl.mw4anim (4557B) + +V4H mechs/jenner2c/animation/j2c_walkstandrevr.mw4anim (4573B) + +V4H mechs/jenner2c/animation/j2c_walkstandrevu.mw4anim (4741B) + +V4H mechs/jenner2c/animation/j2c_walkstandu.mw4anim (4761B) + +V4H mechs/jenner2c/animation/j2c_walku.mw4anim (8549B) + +V4H mechs/jenner2c/jenner2c.animscript (37976B) + -OURS shellscripts/graphics/multiplayer/lobbydecals/decal_46.tga (1375B) + -OURS shellscripts/graphics/multiplayer/lobbydecals/decal_47.tga (4140B) + -OURS shellscripts/graphics/multiplayer/lobbydecals/decal_49.tga (4140B) +== skies.mw4: OURS-ONLY PACKAGE (2975 entries) +== textures.mw4: V4H=10419 OURS=10120 | names: common=10120 V4H_only=299 OURS_only=0 | blobs: identical=8124 V4H_uniq=2313 OURS_uniq=1996 + +V4H mechs/champion/champion_cage.erf (11787B) + +V4H mechs/champion/chp_hip.erf (6655B) + +V4H mechs/champion/chp_hip_dam.erf (6655B) + +V4H mechs/champion/chp_lbtoe.erf (1211B) + +V4H mechs/champion/chp_lbtoe_dam.erf (1211B) + +V4H mechs/champion/chp_ldleg.erf (8372B) + +V4H mechs/champion/chp_ldleg_dam.erf (8372B) + +V4H mechs/champion/chp_lfoot.erf (2017B) + +V4H mechs/champion/chp_lfoot_dam.erf (2017B) + +V4H mechs/champion/chp_lftoe.erf (2309B) + +V4H mechs/champion/chp_lftoe_dam.erf (2309B) + +V4H mechs/champion/chp_lgun.erf (8328B) + +V4H mechs/champion/chp_luarm.erf (853B) + +V4H mechs/champion/chp_luarm_dam.erf (853B) + +V4H mechs/champion/chp_luleg.erf (10067B) + +V4H mechs/champion/chp_luleg_dam.erf (10067B) + +V4H mechs/champion/chp_rbtoe.erf (1211B) + +V4H mechs/champion/chp_rbtoe_dam.erf (1211B) + +V4H mechs/champion/chp_rdleg.erf (8372B) + +V4H mechs/champion/chp_rdleg_dam.erf (8372B) + +V4H mechs/champion/chp_rfoot.erf (2017B) + +V4H mechs/champion/chp_rfoot_dam.erf (2017B) + +V4H mechs/champion/chp_rftoe.erf (2309B) + +V4H mechs/champion/chp_rftoe_dam.erf (2309B) + +V4H mechs/champion/chp_rgun.erf (8328B) + +V4H mechs/champion/chp_ruarm.erf (853B) + +V4H mechs/champion/chp_ruarm_dam.erf (853B) + +V4H mechs/champion/chp_ruleg.erf (10067B) + +V4H mechs/champion/chp_ruleg_dam.erf (10067B) + +V4H mechs/champion/chp_specialone.erf (3759B) + +V4H mechs/champion/chp_specialone_dam.erf (1290B) + +V4H mechs/champion/chp_specialtwo.erf (3759B) + +V4H mechs/champion/chp_specialtwo_dam.erf (1290B) + +V4H mechs/champion/chp_torso.erf (63022B) + +V4H mechs/champion/chp_torso_dam.erf (63022B) + +V4H mechs/champion/runninglights.erf (1552B) + +V4H mechs/champion_destroyed/champion_destroyed.erf (14157B) + +V4H mechs/dasher/das_hip.erf (51546B) + +V4H mechs/dasher/das_hip_dam.erf (51546B) + +V4H mechs/dasher/das_ldleg.erf (20552B) + +V4H mechs/dasher/das_ldleg_dam.erf (20552B) + +V4H mechs/dasher/das_lfoot.erf (11228B) + +V4H mechs/dasher/das_lfoot_dam.erf (11228B) + +V4H mechs/dasher/das_lgun.erf (27396B) + +V4H mechs/dasher/das_lgun_dam.erf (783B) + +V4H mechs/dasher/das_ltoe.erf (5172B) + +V4H mechs/dasher/das_ltoe_dam.erf (5172B) + +V4H mechs/dasher/das_luarm.erf (15258B) + +V4H mechs/dasher/das_luarm_dam.erf (15258B) + +V4H mechs/dasher/das_luleg.erf (10648B) + +V4H mechs/dasher/das_luleg_dam.erf (10648B) + +V4H mechs/dasher/das_rdleg.erf (20260B) + +V4H mechs/dasher/das_rdleg_dam.erf (20260B) + +V4H mechs/dasher/das_rfoot.erf (11228B) + +V4H mechs/dasher/das_rfoot_dam.erf (11228B) + +V4H mechs/dasher/das_rgun.erf (27396B) + +V4H mechs/dasher/das_rtoe.erf (5172B) + +V4H mechs/dasher/das_rtoe_dam.erf (5172B) + +V4H mechs/dasher/das_ruarm.erf (15226B) + +V4H mechs/dasher/das_ruarm_dam.erf (943B) + +V4H mechs/dasher/das_ruleg.erf (10692B) + +V4H mechs/dasher/das_ruleg_dam.erf (10692B) + +V4H mechs/dasher/das_torso.erf (81366B) + +V4H mechs/dasher/das_torso_dam.erf (81366B) + +V4H mechs/dasher/dasher_cage.erf (12523B) + +V4H mechs/dasher/runninglights__.erf (10824B) + +V4H mechs/dasher_destroyed/dasher_destroyed.erf (14624B) + +V4H mechs/griffin/grf_hip.erf (2831B) + +V4H mechs/griffin/grf_hip_dam.erf (2831B) + +V4H mechs/griffin/grf_lbtoe.erf (4029B) + +V4H mechs/griffin/grf_lbtoe_dam.erf (4029B) + +V4H mechs/griffin/grf_ldleg.erf (5784B) + +V4H mechs/griffin/grf_ldleg_dam.erf (5784B) + +V4H mechs/griffin/grf_lfoot.erf (791B) + +V4H mechs/griffin/grf_lfoot_dam.erf (791B) + +V4H mechs/griffin/grf_lftoe.erf (1479B) + +V4H mechs/griffin/grf_lftoe_dam.erf (1479B) + +V4H mechs/griffin/grf_lgun.erf (16111B) + +V4H mechs/griffin/grf_lgun_dam.erf (16111B) + +V4H mechs/griffin/grf_luarm.erf (4819B) + +V4H mechs/griffin/grf_luarm_dam.erf (4819B) + +V4H mechs/griffin/grf_luleg.erf (4933B) + +V4H mechs/griffin/grf_luleg_dam.erf (4933B) + +V4H mechs/griffin/grf_rbtoe.erf (4029B) + +V4H mechs/griffin/grf_rbtoe_dam.erf (4029B) + +V4H mechs/griffin/grf_rdleg.erf (5784B) + +V4H mechs/griffin/grf_rdleg_dam.erf (5784B) + +V4H mechs/griffin/grf_rfoot.erf (791B) + +V4H mechs/griffin/grf_rfoot_dam.erf (791B) + +V4H mechs/griffin/grf_rftoe.erf (1479B) + +V4H mechs/griffin/grf_rftoe_dam.erf (1479B) + +V4H mechs/griffin/grf_rgun.erf (14785B) + +V4H mechs/griffin/grf_rgun_dam.erf (14785B) + +V4H mechs/griffin/grf_ruarm.erf (4819B) + +V4H mechs/griffin/grf_ruarm_dam.erf (4819B) + +V4H mechs/griffin/grf_ruleg.erf (4933B) + +V4H mechs/griffin/grf_ruleg_dam.erf (4933B) + +V4H mechs/griffin/grf_specialone.erf (2511B) + +V4H mechs/griffin/grf_specialone_dam.erf (2559B) + +V4H mechs/griffin/grf_specialtwo.erf (3659B) + +V4H mechs/griffin/grf_specialtwo_dam.erf (3707B) + +V4H mechs/griffin/grf_torso.erf (24032B) + +V4H mechs/griffin/grf_torso_dam.erf (24032B) + +V4H mechs/griffin/griffin_cage.erf (13884B) + +V4H mechs/griffin/runninglights.erf (541B) + +V4H mechs/griffin_destroyed/griffin_destroyed.erf (16697B) + +V4H mechs/jenner2c/jec_hip.erf (3936B) + +V4H mechs/jenner2c/jec_hip_dam.erf (3936B) + +V4H mechs/jenner2c/jec_lbtoe.erf (3191B) + +V4H mechs/jenner2c/jec_lbtoe_dam.erf (3191B) + +V4H mechs/jenner2c/jec_ldleg.erf (8438B) + +V4H mechs/jenner2c/jec_ldleg_dam.erf (8438B) + +V4H mechs/jenner2c/jec_lfoot.erf (2667B) + +V4H mechs/jenner2c/jec_lfoot_dam.erf (2667B) + +V4H mechs/jenner2c/jec_lftoe.erf (3599B) + +V4H mechs/jenner2c/jec_lftoe_dam.erf (3599B) + +V4H mechs/jenner2c/jec_lgun.erf (5943B) + +V4H mechs/jenner2c/jec_lgun_dam.erf (5847B) + +V4H mechs/jenner2c/jec_luleg.erf (13003B) + +V4H mechs/jenner2c/jec_luleg_dam.erf (13003B) + +V4H mechs/jenner2c/jec_rbtoe.erf (3191B) + +V4H mechs/jenner2c/jec_rbtoe_dam.erf (3191B) + +V4H mechs/jenner2c/jec_rdleg.erf (8438B) + +V4H mechs/jenner2c/jec_rdleg_dam.erf (8438B) + +V4H mechs/jenner2c/jec_rfoot.erf (2667B) + +V4H mechs/jenner2c/jec_rfoot_dam.erf (2667B) + +V4H mechs/jenner2c/jec_rftoe.erf (3599B) + +V4H mechs/jenner2c/jec_rftoe_dam.erf (3599B) + +V4H mechs/jenner2c/jec_rgun.erf (5943B) + +V4H mechs/jenner2c/jec_rgun_dam.erf (6287B) + +V4H mechs/jenner2c/jec_ruleg.erf (13003B) + +V4H mechs/jenner2c/jec_ruleg_dam.erf (13003B) + +V4H mechs/jenner2c/jec_torso.erf (39244B) + +V4H mechs/jenner2c/jec_torso_dam.erf (39244B) + +V4H mechs/jenner2c/jenner_2c_cage.erf (13103B) + +V4H mechs/jenner2c/runninglights.erf (3062B) + +V4H mechs/jenner2c_destroyed/jenner2c_destroyed.erf (11866B) + +V4H mechs/marauder/mar_hip.erf (22691B) + +V4H mechs/marauder/mar_hip_dam.erf (6571B) + +V4H mechs/marauder/mar_lbtoe.erf (7962B) + +V4H mechs/marauder/mar_ldleg.erf (39714B) + +V4H mechs/marauder/mar_ldleg_dam.erf (11612B) + +V4H mechs/marauder/mar_lfoot.erf (9492B) + +V4H mechs/marauder/mar_lfoot_dam.erf (2755B) + +V4H mechs/marauder/mar_lgun.erf (18985B) + +V4H mechs/marauder/mar_litoe.erf (4394B) + +V4H mechs/marauder/mar_litoe_dam.erf (1345B) + +V4H mechs/marauder/mar_lotoe.erf (4394B) + +V4H mechs/marauder/mar_luarm.erf (11161B) + +V4H mechs/marauder/mar_luarm_dam.erf (9173B) + +V4H mechs/marauder/mar_luleg.erf (8672B) + +V4H mechs/marauder/mar_luleg_dam.erf (2499B) + +V4H mechs/marauder/mar_rbtoe.erf (7962B) + +V4H mechs/marauder/mar_rdleg.erf (38879B) + +V4H mechs/marauder/mar_rdleg_dam.erf (11676B) + +V4H mechs/marauder/mar_rfoot.erf (9594B) + +V4H mechs/marauder/mar_rfoot_dam.erf (2755B) + +V4H mechs/marauder/mar_rgun.erf (18341B) + +V4H mechs/marauder/mar_ritoe.erf (4394B) + +V4H mechs/marauder/mar_ritoe_dam.erf (1345B) + +V4H mechs/marauder/mar_rotoe.erf (4394B) + +V4H mechs/marauder/mar_rotoe_dam.erf (1345B) + +V4H mechs/marauder/mar_ruarm.erf (11008B) + +V4H mechs/marauder/mar_ruarm_dam.erf (5875B) + +V4H mechs/marauder/mar_ruleg.erf (8672B) + +V4H mechs/marauder/mar_ruleg_dam.erf (2499B) + +V4H mechs/marauder/mar_specialone.erf (21560B) + +V4H mechs/marauder/mar_specialone_dam.erf (7325B) + +V4H mechs/marauder/mar_specialtwo.erf (15696B) + +V4H mechs/marauder/mar_specialtwo_dam.erf (4731B) + +V4H mechs/marauder/mar_torso.erf (54047B) + +V4H mechs/marauder/mar_torso_dam.erf (13479B) + +V4H mechs/marauder/marauder_cage.erf (22678B) + +V4H mechs/marauder/runninglights.erf (541B) + +V4H mechs/marauder_destroyed/marauder_destroyed.erf (18503B) + +V4H mechs/thunderbolt/runninglights.erf (3062B) + +V4H mechs/thunderbolt/thu_hip.erf (5811B) + +V4H mechs/thunderbolt/thu_hip_dam.erf (5811B) + +V4H mechs/thunderbolt/thu_lbtoe.erf (2723B) + +V4H mechs/thunderbolt/thu_ldleg.erf (9171B) + +V4H mechs/thunderbolt/thu_ldleg_dam.erf (9171B) + +V4H mechs/thunderbolt/thu_lfoot.erf (1441B) + +V4H mechs/thunderbolt/thu_lfoot_dam.erf (1441B) + +V4H mechs/thunderbolt/thu_lftoe.erf (2555B) + +V4H mechs/thunderbolt/thu_lftoe_dam.erf (2555B) + +V4H mechs/thunderbolt/thu_lgun.erf (10687B) + +V4H mechs/thunderbolt/thu_luarm.erf (2569B) + +V4H mechs/thunderbolt/thu_luarm_dam.erf (2569B) + +V4H mechs/thunderbolt/thu_luleg.erf (6794B) + +V4H mechs/thunderbolt/thu_luleg_dam.erf (6794B) + +V4H mechs/thunderbolt/thu_rbtoe.erf (2723B) + +V4H mechs/thunderbolt/thu_rbtoe_dam.erf (2723B) + +V4H mechs/thunderbolt/thu_rdleg.erf (9171B) + +V4H mechs/thunderbolt/thu_rdleg_dam.erf (9171B) + +V4H mechs/thunderbolt/thu_rfoot.erf (1441B) + +V4H mechs/thunderbolt/thu_rfoot_dam.erf (1441B) + +V4H mechs/thunderbolt/thu_rftoe.erf (2555B) + +V4H mechs/thunderbolt/thu_rftoe_dam.erf (2555B) + +V4H mechs/thunderbolt/thu_rgun.erf (12097B) + +V4H mechs/thunderbolt/thu_rgun_dam.erf (12097B) + +V4H mechs/thunderbolt/thu_ruarm.erf (2569B) + +V4H mechs/thunderbolt/thu_ruarm_dam.erf (2569B) + +V4H mechs/thunderbolt/thu_ruleg.erf (6794B) + +V4H mechs/thunderbolt/thu_ruleg_dam.erf (6794B) + +V4H mechs/thunderbolt/thu_specialone.erf (4371B) + +V4H mechs/thunderbolt/thu_torso.erf (12695B) + +V4H mechs/thunderbolt/thu_torso_dam.erf (12695B) + +V4H mechs/thunderbolt/thunderbolt_cage.erf (20919B) + +V4H mechs/thunderbolt_destroyed/thunderbolt_destroyed.erf (12593B) + +V4H textures/@achp0.tga (1048594B) + +V4H textures/@achp0.tga{hint} (4B) + +V4H textures/@achp1.tga (262162B) + +V4H textures/@achp1.tga{hint} (4B) + +V4H textures/@achp2.tga (65554B) + +V4H textures/@achp2.tga{hint} (4B) + +V4H textures/@achp3.tga (16402B) + +V4H textures/@achp3.tga{hint} (4B) + +V4H textures/@achp4.tga (4114B) + +V4H textures/@achp4.tga{hint} (4B) + +V4H textures/@achp5.tga (1042B) + +V4H textures/@achp5.tga{hint} (4B) + +V4H textures/@agrf0.tga (1048594B) + +V4H textures/@agrf0.tga{hint} (4B) + +V4H textures/@agrf1.tga (262162B) + +V4H textures/@agrf1.tga{hint} (4B) + +V4H textures/@agrf2.tga (65554B) + +V4H textures/@agrf2.tga{hint} (4B) + +V4H textures/@agrf3.tga (16402B) + +V4H textures/@agrf3.tga{hint} (4B) + +V4H textures/@agrf4.tga (4114B) + +V4H textures/@agrf4.tga{hint} (4B) + +V4H textures/@agrf5.tga (1042B) + +V4H textures/@agrf5.tga{hint} (4B) + +V4H textures/@ajec0.tga (1048594B) + +V4H textures/@ajec0.tga{hint} (4B) + +V4H textures/@ajec1.tga (262162B) + +V4H textures/@ajec1.tga{hint} (4B) + +V4H textures/@ajec2.tga (65554B) + +V4H textures/@ajec2.tga{hint} (4B) + +V4H textures/@ajec3.tga (16402B) + +V4H textures/@ajec3.tga{hint} (4B) + +V4H textures/@ajec4.tga (4114B) + +V4H textures/@ajec4.tga{hint} (4B) + +V4H textures/@ajec5.tga (1042B) + +V4H textures/@ajec5.tga{hint} (4B) + +V4H textures/@amar0.tga (926350B) + +V4H textures/@amar0.tga{hint} (4B) + +V4H textures/@amar1.tga (247836B) + +V4H textures/@amar1.tga{hint} (4B) + +V4H textures/@amar2.tga (66075B) + +V4H textures/@amar2.tga{hint} (4B) + +V4H textures/@amar3.tga (16923B) + +V4H textures/@amar3.tga{hint} (4B) + +V4H textures/@amar4.tga (4635B) + +V4H textures/@amar4.tga{hint} (4B) + +V4H textures/@amar5.tga (1563B) + +V4H textures/@amar5.tga{hint} (4B) + +V4H textures/@athu0.tga (1048594B) + +V4H textures/@athu0.tga{hint} (4B) + +V4H textures/@athu1.tga (262162B) + +V4H textures/@athu1.tga{hint} (4B) + +V4H textures/@athu2.tga (65554B) + +V4H textures/@athu2.tga{hint} (4B) + +V4H textures/@athu3.tga (16402B) + +V4H textures/@athu3.tga{hint} (4B) + +V4H textures/@athu4.tga (4114B) + +V4H textures/@athu4.tga{hint} (4B) + +V4H textures/@athu5.tga (1042B) + +V4H textures/@athu5.tga{hint} (4B) + +V4H textures/footsteps/champion_default.tga (16428B) + +V4H textures/footsteps/champion_default.tga{hint} (4B) + +V4H textures/footsteps/champion_dirt.tga (16923B) + +V4H textures/footsteps/champion_dirt.tga{hint} (4B) + +V4H textures/footsteps/champion_snow.tga (16923B) + +V4H textures/footsteps/champion_snow.tga{hint} (4B) + +V4H textures/footsteps/griffin_default.tga (16428B) + +V4H textures/footsteps/griffin_default.tga{hint} (4B) + +V4H textures/footsteps/griffin_dirt.tga (16923B) + +V4H textures/footsteps/griffin_dirt.tga{hint} (4B) + +V4H textures/footsteps/griffin_snow.tga (16923B) + +V4H textures/footsteps/griffin_snow.tga{hint} (4B) + +V4H textures/footsteps/jenner2c_default.tga (16428B) + +V4H textures/footsteps/jenner2c_default.tga{hint} (4B) + +V4H textures/footsteps/jenner2c_dirt.tga (16428B) + +V4H textures/footsteps/jenner2c_dirt.tga{hint} (4B) + +V4H textures/footsteps/jenner2c_snow.tga (16428B) + +V4H textures/footsteps/jenner2c_snow.tga{hint} (4B) + +V4H textures/footsteps/marauder_default.tga (16923B) + +V4H textures/footsteps/marauder_default.tga{hint} (4B) + +V4H textures/footsteps/marauder_dirt.tga (16923B) + +V4H textures/footsteps/marauder_dirt.tga{hint} (4B) + +V4H textures/footsteps/marauder_snow.tga (16923B) + +V4H textures/footsteps/marauder_snow.tga{hint} (4B) + +V4H textures/footsteps/thunderbolt_default.tga (16428B) + +V4H textures/footsteps/thunderbolt_default.tga{hint} (4B) + +V4H textures/footsteps/thunderbolt_dirt.tga (16428B) + +V4H textures/footsteps/thunderbolt_dirt.tga{hint} (4B) + +V4H textures/footsteps/thunderbolt_snow.tga (16428B) + +V4H textures/footsteps/thunderbolt_snow.tga{hint} (4B) +== usermissions/desert.mw4: OURS-ONLY PACKAGE (128 entries) +== usermissions/phoenixpalacestb.mw4: OURS-ONLY PACKAGE (67 entries) +== usermissions/s1s1.mw4: OURS-ONLY PACKAGE (74 entries) +== usermissions/s1s2.mw4: OURS-ONLY PACKAGE (76 entries) +== usermissions/s1s3.mw4: OURS-ONLY PACKAGE (72 entries) +== variants/annihilator (gausszilla).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator anh-1a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator anh-1e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator anh-1g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator anh-1x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator anh-2a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator anh-2ax.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator anh-3a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator anh-4a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator c 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/annihilator c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer (morgan).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer (wolf).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-2k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-2r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-2rb.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-2s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-2w.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-4m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-5cs.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-5r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-5s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-5w.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-6s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-7 l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-7c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-7s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-8m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-9k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-9m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer arc-9w.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/archer c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/arctic wolf prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/arctic wolf 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/arctic wolf 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/arctic wolf a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/arctic wolf ii prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/arctic wolf ii a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/arctic wolf ii b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/arctic wolf ii c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/argus ags-2d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/argus ags-4d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/argus ags-5d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/argus ags-6f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/assassinii (alice).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/assassinii (servitor).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/assassinii asn-101.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/assassinii asn-21.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/assassinii asn-23.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/assassinii asn-30.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/assassinii asn-99.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas (danielle).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas (devlin).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas (jedra).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas (jurn).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas (kerensky).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-d-dc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-dr.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-k-dc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-k2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-rs.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-s2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-s3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as7-wgs.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas as8-d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas ii as7-d-h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas ii as7-d-h2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/atlas pharaoh.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-o prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-oa.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-ob.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-oc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-od.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-oe.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-of.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-og.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-oi.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar av1-or.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar avatar 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/avatar satyr 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome (buck).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome (cameron).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome (klatt).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-10km.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-11m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-8q.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-8r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-8t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-8v.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-9m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-9ma.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/awesome aws-9q.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster (calvin 2).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster (calvin).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster (red corsair).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster (rogers).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-10s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-10s2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-1d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-1g-dc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-1g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-1gb.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-1gbc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-1gc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-1s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-2c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-3m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-3s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-4s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-5m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-6c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster blr-6x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg (conal).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster ic garg h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemaster stock test.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemasteriic c3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemasteriic frost.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/battlemasteriic stock test.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/behemoth 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/behemoth 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/behemoth i 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/behemoth i 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/behemoth i 5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/behemoth i 6.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/behemoth i 7.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/behemothii redgrave.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-o prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-oa.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-ob.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-oc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-od.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-oe.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-of.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-og.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-or.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk bhku-ox.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk black hawk 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk i.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk nova 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk standard 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk standard 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black hawk standard 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black knight (ross).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black knight bl-12-knt.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black knight bl-6-knt.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black knight bl-6-rr.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black knight bl-6b-knt.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black knight bl-7-knt-l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black knight bl-7-knt.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black knight bl-9-knt.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner duck.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/black lanner x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/brigand ldt-1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/brigand ldt-x1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/brigand ldt-x3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/brigand ldt-x4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/brigand ldt-xpr1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/brigand ldt-xpr2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/bushwacker bsw-l1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/bushwacker bsw-s2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/bushwacker bsw-s2r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/bushwacker bsw-x1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/bushwacker bsw-x2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/bushwacker wildonion.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult (butterbee).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-a1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c1b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c4c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c5a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-c6.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-k 5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-k2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-k2k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-k3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult cplt-k4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/catapult duck.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cauldron-born b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cauldron-born prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cauldron-born (samantha).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cauldron-born a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cauldron-born c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cauldron-born d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cauldron-born h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cauldron-born redgrave.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/champion c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/champion chp-1 n 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/champion chp1n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/champion chp2n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/champion chp3n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/chimera cma-1s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/chimera cma-c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando (freyr).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-1b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-1c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-1d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-2d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-2dr.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-3a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-5s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-7 s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando com-7b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando frost.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/commando pyrotech.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cougar xr.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cyclops cp-10-q.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cyclops cp-10-z.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cyclops cp-11-a-dc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cyclops cp-11-a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cyclops cp-11-b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cyclops cp-11-g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/cyclops cp-12-k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi (hohiro).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi (prometheus).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi (widowmaker).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi cliffjumper.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi duck.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi w.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/daishi x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher (aletha).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher i.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dasher m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/deimos prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/deimos 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/deimos a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/deimos b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/deimos e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon (douglas).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon (mark).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon (yoriyoshi).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon drg-1c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon drg-1n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon drg-5n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon drg-5nr.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon drg-7n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon gd drg-1g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon gd drg-5k-dc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon gd drg-5k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon gd drg-7k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon gd drg-7kc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon gd drg-9kc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon gd drg-c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon ii .mw4: V4H-ONLY PACKAGE (4 entries) +== variants/dragon ii 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/fafnir fnr-5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/fafnir fnr-5b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/fafnir fnr-5x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/fafnir fnr-6u.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/flea (fire ant).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/flea fle-15.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/flea fle-16.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/flea fle-17.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/flea fle-20.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/flea fle-4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/flea marvin.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator i.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/gladiator tc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin (francine ii).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin (francine).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-1ds.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-1e (sparky).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-1e2 (sparky2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-1n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-1s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-2n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-3m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-5m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin grf-6s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin iic 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin iic 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin iic 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/griffin iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/grizzly 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/grizzly 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hauptmann ha1-o.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hauptmann ha1-oa.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hauptmann ha1-ob.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hauptmann ha1-oc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hauptmann ha1-oe.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hauptmann ha1-of.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hauptmann sumo.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellhound 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellhound 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellhound 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellhound 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellhound 5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellhound 7.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellhound pyrotech.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellhound shyumbrion.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellspawn hsn-10g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellspawn hsn-10sr.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellspawn hsn-7d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellspawn hsn-8e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hellspawn hsn-9f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander (colleen).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander (jorgensson).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-641-x-2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-694.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-732.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-732b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-733.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-733c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-733p.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-734.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-736.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander hgn-738.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/highlander iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hollanderii bzk-f3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hollanderii bzk-g1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hollanderii ii bzk-f5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hollanderii ii bzk-f7.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hollanderii iii bzk-d1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hollanderii iii bzk-d2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hollanderii iii bzk-d3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hollanderii plainsrider.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback (hohiro).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-4g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-4h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-4j.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-4n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-4p.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-4sp.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-5m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-5n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-5p.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-5s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-5sg.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-5ss.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-6n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-6s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-7r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-7s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback hbk-7x-4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback iic 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback iic 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/hunchback iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/jenner2c 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/jenner2c 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/jenner2c pharaoh.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak (cale).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak 5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak hard ass.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak ii.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/kodiak kiki.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki frost.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki hellbringer 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki loki 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki mischief 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki mk ii prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki mk ii a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/loki mk ii b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/longbow lgb-0w.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/longbow lgb-12 c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/longbow lgb-13c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/longbow lgb-13nais.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/longbow lgb-14c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/longbow lgb-7q.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/longbow lgb-7v.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/longbow lgb-8v.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat (bounty hunter).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat (pryde).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat madcat 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat mkii 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat mkii 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat mkii 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat mkii enhanced.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat mkii wildonion.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat mkiv prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat mkiv a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat mkiv c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat sumo.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat tc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat timberwolf 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat w.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat z.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mad cat zanin neko 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-11d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-1r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-2r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-2t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-3d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-3l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-3m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-3r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-5cs.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-5s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-7s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-9d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/marauder mad-9s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari (tara).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/masakari pyrotech.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mauler (todesbote).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mauler daboku dcms-mx90.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mauler mal-1k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mauler mal-1r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mauler mal-2r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mauler mal-3r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/mauler reddog.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/nova cat prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/nova cat a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/nova cat b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/nova cat c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/nova cat d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/nova cat e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/nova cat f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/nova cat h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/osiris osr-3d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/osiris osr-4d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/osiris osr-5d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens kotori 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens ow-1 prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens ow-1a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens ow-1b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens ow-1c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens ow-1d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens ow-1e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens ow-1f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens ow-1r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/owens owens 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma i.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma tc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/puma wildonion.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-1x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-2x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-3l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-3m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-3x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-4l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-4lr.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-sr (shattere.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven rvn-ss (shattere.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven sumo.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/raven x rvn-3x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman (legend kill 2).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman (legend kill).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman c 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman c3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman ii (kataga).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman ii rfl-3n-2 (lk).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman ii rfl-3n-2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman iic 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman iic 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman iic 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman iic 5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-3c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-3cr.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-3n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-4d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-5cs.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-5d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-5m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-6d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-6x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-7g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-7m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-7n.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-7n2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-7x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-8x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman rfl-9t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/rifleman wildonion.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken (attwater).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken (kotare).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken anubis.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken frost.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken i.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken ii (tassa).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken ii .mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken ii 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken p.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken tc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/ryoken z.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat i.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat ii 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat ii 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat ii 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat iii prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat iii a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat iii b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat iii c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat pharaoh.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/shadow cat tc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/solitaire 1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/solitaire 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder (samual).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder chinra camp.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder denkou 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder sd1-o prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder sd1-o a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder sd1-o c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder sd1-o d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder sd1-o e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder sd1-o r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder sd1-of.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/sunder sunder 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar (grayson).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar iii tlr2-o prim.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar iii tlr2-oa.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar iii tlr2-od.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/templar tlr1-o i.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thanatos black drake.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thanatos plainsrider.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thanatos tns-4s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thanatos tns-4t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thanatos tns-6s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor aa.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor f.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor ii prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor ii a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor ii b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor ii c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor ii d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor pharaoh.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor pyrotech.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor q.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor summoner 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thor thor 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt (ilyena).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-10m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-10s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-10se.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-11se.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-17s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-1c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-5d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-5s-t (tallma.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-5s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-5sb.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-5se.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-5ss.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-60-rla.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-7m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-7se.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-9m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-9s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-9se.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/thunderbolt tdr-9t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller e.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller g.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller i.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller little stinker.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uller w.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech um-aiv.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech um-r60.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech um-r60l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech um-r63.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech um-r68.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech um-r69.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech um-r80.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/urbanmech um-r93.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uziel (jacob 2).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uziel sumo.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uziel uzl-2s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uziel uzl-3s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/uziel uzl-8s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor (li).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-10d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-10l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-10s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-9a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-9a1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-9b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-9k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-9k2 (st. jam.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-9s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/victor vtr-c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture chinra.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture dd.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture iii prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture iii a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture iii c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture mad dog 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture mk iv prime.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture mk iv a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture mk iv c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture mk iv d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture v.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/vulture vulture 4.10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer c.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer c2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer c3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic 10.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic 11.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic 12.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic 13.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic 2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic 3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic 4.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic 7.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer iic.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whd-10ct.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-10k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-10t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-11t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-4l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-5l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-6d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-6k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-6l.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-6r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-6rb.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-7a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-7cs.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-7k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-7m-dc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-7m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-7s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-8d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-8k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-8m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-8r.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-9d.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-9k.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/warhammer whm-9s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound (allard).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound iic (grinner).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-1.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-1a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-1b.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-2h.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-2x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-3 s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-3m.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-4w.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-4wa.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/wolfhound wlf-5.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus (leonidas).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus (stacy).mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-10wb.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-11s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-5s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-5t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-6a.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-6s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-6t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-6y.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-9s-dc.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-9s.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-9s2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-9t.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-9wd.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-x.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-x2.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-x3.mw4: V4H-ONLY PACKAGE (4 entries) +== variants/zeus zeu-x4.mw4: V4H-ONLY PACKAGE (4 entries) diff --git a/MW4COMPARE/reports/props-decoded-diff.txt b/MW4COMPARE/reports/props-decoded-diff.txt new file mode 100644 index 00000000..de85890d --- /dev/null +++ b/MW4COMPARE/reports/props-decoded-diff.txt @@ -0,0 +1,578 @@ +# entries: A=10779 B=10627 A_only=155 B_only=3 decoded-differ=419 ++A mechs/jenner2c/animation/j2c_back.mw4anim 8219B ++A mechs/jenner2c/animation/j2c_backd.mw4anim 8021B ++A mechs/jenner2c/animation/j2c_backl.mw4anim 8001B ++A mechs/jenner2c/animation/j2c_backr.mw4anim 8561B ++A mechs/jenner2c/animation/j2c_backstand.mw4anim 4491B ++A mechs/jenner2c/animation/j2c_backstandd.mw4anim 4605B ++A mechs/jenner2c/animation/j2c_backstandl.mw4anim 4389B ++A mechs/jenner2c/animation/j2c_backstandr.mw4anim 4249B ++A mechs/jenner2c/animation/j2c_backstandrev.mw4anim 4491B ++A mechs/jenner2c/animation/j2c_backstandrevd.mw4anim 4605B ++A mechs/jenner2c/animation/j2c_backstandrevl.mw4anim 4249B ++A mechs/jenner2c/animation/j2c_backstandrevr.mw4anim 4389B ++A mechs/jenner2c/animation/j2c_backstandrevu.mw4anim 4665B ++A mechs/jenner2c/animation/j2c_backstandu.mw4anim 4665B ++A mechs/jenner2c/animation/j2c_backu.mw4anim 8665B ++A mechs/jenner2c/animation/j2c_fallback.mw4anim 18365B ++A mechs/jenner2c/animation/j2c_fallforward.mw4anim 17909B ++A mechs/jenner2c/animation/j2c_fallleft.mw4anim 18445B ++A mechs/jenner2c/animation/j2c_fallpose.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_fallright.mw4anim 18445B ++A mechs/jenner2c/animation/j2c_getup.mw4anim 13201B ++A mechs/jenner2c/animation/j2c_getupright.mw4anim 13201B ++A mechs/jenner2c/animation/j2c_jump.mw4anim 4181B ++A mechs/jenner2c/animation/j2c_landforward.mw4anim 8067B ++A mechs/jenner2c/animation/j2c_landstand.mw4anim 6143B ++A mechs/jenner2c/animation/j2c_landstandd.mw4anim 6223B ++A mechs/jenner2c/animation/j2c_landstandl.mw4anim 6443B ++A mechs/jenner2c/animation/j2c_landstandr.mw4anim 6443B ++A mechs/jenner2c/animation/j2c_landstandu.mw4anim 6163B ++A mechs/jenner2c/animation/j2c_lgimp.mw4anim 7991B ++A mechs/jenner2c/animation/j2c_lgimpd.mw4anim 8009B ++A mechs/jenner2c/animation/j2c_lgimpl.mw4anim 7769B ++A mechs/jenner2c/animation/j2c_lgimppose.mw4anim 1543B ++A mechs/jenner2c/animation/j2c_lgimpposed.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_lgimpposel.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_lgimpposer.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_lgimpposeu.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_lgimpr.mw4anim 7969B ++A mechs/jenner2c/animation/j2c_lgimpstand.mw4anim 4247B ++A mechs/jenner2c/animation/j2c_lgimpstandd.mw4anim 4277B ++A mechs/jenner2c/animation/j2c_lgimpstandl.mw4anim 4001B ++A mechs/jenner2c/animation/j2c_lgimpstandr.mw4anim 4213B ++A mechs/jenner2c/animation/j2c_lgimpstandu.mw4anim 4237B ++A mechs/jenner2c/animation/j2c_lgimpturnleft.mw4anim 4423B ++A mechs/jenner2c/animation/j2c_lgimpturnleftd.mw4anim 4241B ++A mechs/jenner2c/animation/j2c_lgimpturnleftl.mw4anim 4321B ++A mechs/jenner2c/animation/j2c_lgimpturnleftr.mw4anim 4241B ++A mechs/jenner2c/animation/j2c_lgimpturnleftu.mw4anim 4241B ++A mechs/jenner2c/animation/j2c_lgimpturnright.mw4anim 3895B ++A mechs/jenner2c/animation/j2c_lgimpturnrightd.mw4anim 3789B ++A mechs/jenner2c/animation/j2c_lgimpturnrightl.mw4anim 3809B ++A mechs/jenner2c/animation/j2c_lgimpturnrightr.mw4anim 3749B ++A mechs/jenner2c/animation/j2c_lgimpturnrightu.mw4anim 3689B ++A mechs/jenner2c/animation/j2c_lgimpu.mw4anim 7989B ++A mechs/jenner2c/animation/j2c_powerdown.mw4anim 3025B ++A mechs/jenner2c/animation/j2c_powerdownd.mw4anim 3025B ++A mechs/jenner2c/animation/j2c_powerdownl.mw4anim 3145B ++A mechs/jenner2c/animation/j2c_powerdownr.mw4anim 3145B ++A mechs/jenner2c/animation/j2c_powerdownu.mw4anim 3025B ++A mechs/jenner2c/animation/j2c_powerup.mw4anim 3025B ++A mechs/jenner2c/animation/j2c_powerupd.mw4anim 3045B ++A mechs/jenner2c/animation/j2c_powerupl.mw4anim 3165B ++A mechs/jenner2c/animation/j2c_powerupr.mw4anim 3165B ++A mechs/jenner2c/animation/j2c_powerupu.mw4anim 3025B ++A mechs/jenner2c/animation/j2c_rgimp.mw4anim 7991B ++A mechs/jenner2c/animation/j2c_rgimpd.mw4anim 8009B ++A mechs/jenner2c/animation/j2c_rgimpl.mw4anim 7969B ++A mechs/jenner2c/animation/j2c_rgimppose.mw4anim 1543B ++A mechs/jenner2c/animation/j2c_rgimpposed.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_rgimpposel.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_rgimpposer.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_rgimpposeu.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_rgimpr.mw4anim 7769B ++A mechs/jenner2c/animation/j2c_rgimpstand.mw4anim 4227B ++A mechs/jenner2c/animation/j2c_rgimpstandd.mw4anim 4277B ++A mechs/jenner2c/animation/j2c_rgimpstandl.mw4anim 4213B ++A mechs/jenner2c/animation/j2c_rgimpstandr.mw4anim 4001B ++A mechs/jenner2c/animation/j2c_rgimpstandu.mw4anim 4237B ++A mechs/jenner2c/animation/j2c_rgimpturnleft.mw4anim 3895B ++A mechs/jenner2c/animation/j2c_rgimpturnleftd.mw4anim 3789B ++A mechs/jenner2c/animation/j2c_rgimpturnleftl.mw4anim 3749B ++A mechs/jenner2c/animation/j2c_rgimpturnleftr.mw4anim 3809B ++A mechs/jenner2c/animation/j2c_rgimpturnleftu.mw4anim 3689B ++A mechs/jenner2c/animation/j2c_rgimpturnright.mw4anim 4423B ++A mechs/jenner2c/animation/j2c_rgimpturnrightd.mw4anim 4241B ++A mechs/jenner2c/animation/j2c_rgimpturnrightl.mw4anim 4241B ++A mechs/jenner2c/animation/j2c_rgimpturnrightr.mw4anim 4321B ++A mechs/jenner2c/animation/j2c_rgimpturnrightu.mw4anim 4241B ++A mechs/jenner2c/animation/j2c_rgimpu.mw4anim 7989B ++A mechs/jenner2c/animation/j2c_run.mw4anim 8443B ++A mechs/jenner2c/animation/j2c_rund.mw4anim 7705B ++A mechs/jenner2c/animation/j2c_runl.mw4anim 8089B ++A mechs/jenner2c/animation/j2c_runr.mw4anim 7849B ++A mechs/jenner2c/animation/j2c_runu.mw4anim 8209B ++A mechs/jenner2c/animation/j2c_squatdown.mw4anim 6813B ++A mechs/jenner2c/animation/j2c_squatdownd.mw4anim 6713B ++A mechs/jenner2c/animation/j2c_squatdownl.mw4anim 7293B ++A mechs/jenner2c/animation/j2c_squatdownr.mw4anim 6853B ++A mechs/jenner2c/animation/j2c_squatdownu.mw4anim 7153B ++A mechs/jenner2c/animation/j2c_squatup.mw4anim 6733B ++A mechs/jenner2c/animation/j2c_squatupd.mw4anim 6653B ++A mechs/jenner2c/animation/j2c_squatupl.mw4anim 7173B ++A mechs/jenner2c/animation/j2c_squatupr.mw4anim 6733B ++A mechs/jenner2c/animation/j2c_squatupu.mw4anim 7033B ++A mechs/jenner2c/animation/j2c_standback.mw4anim 4731B ++A mechs/jenner2c/animation/j2c_standbackd.mw4anim 4877B ++A mechs/jenner2c/animation/j2c_standbackl.mw4anim 4669B ++A mechs/jenner2c/animation/j2c_standbackr.mw4anim 4617B ++A mechs/jenner2c/animation/j2c_standbacku.mw4anim 4557B ++A mechs/jenner2c/animation/j2c_standlgimp.mw4anim 5007B ++A mechs/jenner2c/animation/j2c_standlgimpd.mw4anim 4733B ++A mechs/jenner2c/animation/j2c_standlgimpl.mw4anim 4745B ++A mechs/jenner2c/animation/j2c_standlgimpr.mw4anim 4861B ++A mechs/jenner2c/animation/j2c_standlgimpu.mw4anim 4909B ++A mechs/jenner2c/animation/j2c_standpose.mw4anim 1543B ++A mechs/jenner2c/animation/j2c_standposed.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_standposel.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_standposer.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_standposeu.mw4anim 1377B ++A mechs/jenner2c/animation/j2c_standrgimp.mw4anim 5007B ++A mechs/jenner2c/animation/j2c_standrgimpd.mw4anim 4733B ++A mechs/jenner2c/animation/j2c_standrgimpl.mw4anim 4861B ++A mechs/jenner2c/animation/j2c_standrgimpr.mw4anim 4745B ++A mechs/jenner2c/animation/j2c_standrgimpu.mw4anim 4909B ++A mechs/jenner2c/animation/j2c_standwalk.mw4anim 4491B ++A mechs/jenner2c/animation/j2c_standwalkd.mw4anim 4609B ++A mechs/jenner2c/animation/j2c_standwalkl.mw4anim 4369B ++A mechs/jenner2c/animation/j2c_standwalkr.mw4anim 4229B ++A mechs/jenner2c/animation/j2c_standwalku.mw4anim 4605B ++A mechs/jenner2c/animation/j2c_turnleft.mw4anim 3795B ++A mechs/jenner2c/animation/j2c_turnleftd.mw4anim 3533B ++A mechs/jenner2c/animation/j2c_turnleftl.mw4anim 3733B ++A mechs/jenner2c/animation/j2c_turnleftr.mw4anim 3733B ++A mechs/jenner2c/animation/j2c_turnleftu.mw4anim 3693B ++A mechs/jenner2c/animation/j2c_turnright.mw4anim 3795B ++A mechs/jenner2c/animation/j2c_turnrightd.mw4anim 3533B ++A mechs/jenner2c/animation/j2c_turnrightl.mw4anim 3733B ++A mechs/jenner2c/animation/j2c_turnrightr.mw4anim 3733B ++A mechs/jenner2c/animation/j2c_turnrightu.mw4anim 3693B ++A mechs/jenner2c/animation/j2c_walk.mw4anim 8783B ++A mechs/jenner2c/animation/j2c_walkd.mw4anim 8693B ++A mechs/jenner2c/animation/j2c_walkl.mw4anim 8589B ++A mechs/jenner2c/animation/j2c_walkr.mw4anim 8585B ++A mechs/jenner2c/animation/j2c_walkstand.mw4anim 4847B ++A mechs/jenner2c/animation/j2c_walkstandd.mw4anim 4597B ++A mechs/jenner2c/animation/j2c_walkstandl.mw4anim 4573B ++A mechs/jenner2c/animation/j2c_walkstandr.mw4anim 4557B ++A mechs/jenner2c/animation/j2c_walkstandrev.mw4anim 4675B ++A mechs/jenner2c/animation/j2c_walkstandrevd.mw4anim 4597B ++A mechs/jenner2c/animation/j2c_walkstandrevl.mw4anim 4557B ++A mechs/jenner2c/animation/j2c_walkstandrevr.mw4anim 4573B ++A mechs/jenner2c/animation/j2c_walkstandrevu.mw4anim 4741B ++A mechs/jenner2c/animation/j2c_walkstandu.mw4anim 4761B ++A mechs/jenner2c/animation/j2c_walku.mw4anim 8549B ++A mechs/jenner2c/jenner2c.animscript 37976B +-B shellscripts/graphics/multiplayer/lobbydecals/decal_46.tga 1375B +-B shellscripts/graphics/multiplayer/lobbydecals/decal_47.tga 4140B +-B shellscripts/graphics/multiplayer/lobbydecals/decal_49.tga 4140B +~ aiplayers/playerai/playerai.data{gamemodel} A=28B B=28B +~ buildings/base_turret/base_turret.subsystems A=978B B=978B +~ buildings/calliope/calliope.subsystems A=3794B B=3794B +~ buildings/calliope/calliope.torso{gamemodel} A=1608B B=1608B +~ buildings/elitex_mp_turret/elitex_mp_turret.subsystems A=1358B B=1358B +~ buildings/factory1_destroyed/factory1_destroyed.instance A=96B B=96B +~ buildings/factory2_destroyed/factory2_destroyed.instance A=96B B=96B +~ buildings/factory4_destroyed/factory4_destroyed.instance A=96B B=96B +~ buildings/factory6_destroyed/factory6_destroyed.instance A=96B B=96B +~ buildings/factory7_destroyed/factory7_destroyed.instance A=96B B=96B +~ buildings/farm3_destroyed/farm3_destroyed.data{gamemodel} A=28B B=28B +~ buildings/farm4_destroyed/farm4_destroyed.data{gamemodel} A=28B B=28B +~ buildings/fuel_tank_destroyed/fuel_tank_destroyed.data{gamemodel} A=28B B=28B +~ buildings/hrothgart/gun1.torso{gamemodel} A=1608B B=1608B +~ buildings/hrothgart/gun2.torso{gamemodel} A=1608B B=1608B +~ buildings/hrothgart/gun3.torso{gamemodel} A=1608B B=1608B +~ buildings/hrothgart/gun4.torso{gamemodel} A=1608B B=1608B +~ buildings/hrothgart/hrothgart.contents[joint_dropship]{armature} A=282B B=282B +~ buildings/hrothgart/hrothgart.contents[joint_engines]{armature} A=1122B B=1122B +~ buildings/hrothgart/hrothgart.subsystems A=9438B B=9438B +~ buildings/large_guardtower/large_guardtower.contents A=282B B=282B +~ buildings/large_guardtower/large_guardtower.contents[joint_base]{armature} A=282B B=282B +~ buildings/large_guardtower/large_guardtower.instance A=308B B=308B +~ buildings/large_guardtower/large_guardtower.subsystems A=3034B B=3034B +~ buildings/large_guardtower/large_guardtower.torso{gamemodel} A=1608B B=1608B +~ buildings/listening_post/listening_post.data{gamemodel} A=664B B=664B +~ buildings/lrm_turret/lrm_turret.data{gamemodel} A=664B B=664B +~ buildings/lrm_turret/lrm_turret.subsystems A=1030B B=1030B +~ buildings/lrm_turret/lrm_turret.torso{gamemodel} A=1608B B=1608B +~ buildings/marsh_lrm_turret/marsh_lrm_turret.contents[joint_base]{armature} A=282B B=282B +~ buildings/marsh_lrm_turret/marsh_lrm_turret.subsystems A=1030B B=1030B +~ buildings/marsh_lrm_turret/marsh_lrm_turret.torso{gamemodel} A=1608B B=1608B +~ buildings/marsh_radar_station/marsh_radar_station.contents A=282B B=282B +~ buildings/marsh_radar_station/marsh_radar_station.contents[joint_base]{armature} A=282B B=282B +~ buildings/mercwall_towerb/mercwall_towerb.instance A=308B B=308B +~ buildings/ml_cannon/ml_cannon.subsystems A=598B B=598B +~ buildings/ml_cannon/ml_cannon.torso{gamemodel} A=1608B B=1608B +~ buildings/overlordt/overlordt.contents[joint_overlord]{armature} A=1962B B=1962B +~ buildings/overlordt/overlordt.subsystems A=4446B B=4446B +~ buildings/radiotower/radiotower.instance A=308B B=308B +~ buildings/repair_platform/repair_platform.instance A=308B B=308B +~ buildings/roadblock_1/roadblock_1.instance A=308B B=308B +~ buildings/roadblock_2/roadblock_2.instance A=308B B=308B +~ buildings/ruined1/ruined1.instance A=308B B=308B +~ buildings/ruined2/ruined2.instance A=308B B=308B +~ buildings/sp_turret/sp_turret.subsystems A=978B B=978B +~ buildings/strongpoint_defense/strongpoint_defense.contents[joint_root]{armature} A=842B B=842B +~ buildings/strongpoint_defense/strongpoint_defense.subsystems A=2982B B=2982B +~ buildings/strongpoint_defense/strongpoint_defense.torso{gamemodel} A=1608B B=1608B +~ buildings/talont/talont.contents[joint_hull]{armature} A=2802B B=2802B +~ buildings/talont/talont.subsystems A=5418B B=5418B +~ buildings/trailer_turret/trailer_turret.contents[joint_base]{armature} A=282B B=282B +~ buildings/trailer_turret/trailer_turret.subsystems A=1030B B=1030B +~ buildings/trailer_turret/trailer_turret.torso{gamemodel} A=1608B B=1608B +~ buildings/urban_lg_04_destroyed/urban_lg_04_destroyed.instance A=96B B=96B +~ culturals/rock_volcan_small03/rock_volcan_small03.data{gamemodel} A=40B B=40B +~ culturals/rock_volcan_small03/rock_volcan_small03.instance A=100B B=100B +~ culturals/tree_alpine_evergreen02/tree_alpine_evergreen02.data{gamemodel} A=40B B=40B +~ culturals/tree_alpine_evergreen02/tree_alpine_evergreen02.instance A=100B B=100B +~ culturals/tree_arctic_snowy02/tree_arctic_snowy02.data{gamemodel} A=40B B=40B +~ culturals/tree_arctic_snowy02/tree_arctic_snowy02.instance A=100B B=100B +~ culturals/tree_arctic_snowy03/tree_arctic_snowy03.data{gamemodel} A=40B B=40B +~ culturals/tree_arctic_snowy03/tree_arctic_snowy03.instance A=100B B=100B +~ culturals/tree_arctic_snowy_multi01/tree_arctic_snowy_multi01.instance A=100B B=100B +~ culturals/tree_tropical_beachshrub/tree_tropical_beachshrub.data{gamemodel} A=40B B=40B +~ effects/buildingexplosions/air_control_tower/air_control_tower.audio A=128B B=128B +~ effects/buildingexplosions/barriercap/barriercap.audio A=128B B=128B +~ effects/buildingexplosions/bridge/bridge.audio A=128B B=128B +~ effects/buildingexplosions/colpillar/colpillar.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/colsm90_torch/colsm90_torch.audio A=128B B=128B +~ effects/buildingexplosions/command_center/command_center.audio A=128B B=128B +~ effects/buildingexplosions/communications_dish/communications_dish.audio A=128B B=128B +~ effects/buildingexplosions/crate_stack/crate_stack.audio A=128B B=128B +~ effects/buildingexplosions/factory1/factory1.audio A=128B B=128B +~ effects/buildingexplosions/factory2/factory2.audio A=128B B=128B +~ effects/buildingexplosions/factory3/factory3.audio A=128B B=128B +~ effects/buildingexplosions/factory4/factory4.audio A=128B B=128B +~ effects/buildingexplosions/factory5/factory5.audio A=128B B=128B +~ effects/buildingexplosions/factory6/factory6.audio A=128B B=128B +~ effects/buildingexplosions/factory7/factory7.audio A=128B B=128B +~ effects/buildingexplosions/fuel_tanks/fuel_tanks.audio A=128B B=128B +~ effects/buildingexplosions/generator_control/generator_control.audio A=128B B=128B +~ effects/buildingexplosions/generator_control/generator_control.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_building/generic_building.audio A=128B B=128B +~ effects/buildingexplosions/generic_building/generic_building.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_building2/generic_building2.audio A=128B B=128B +~ effects/buildingexplosions/generic_building2/generic_building2.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_flatlarge/generic_flatlarge.audio A=128B B=128B +~ effects/buildingexplosions/generic_flatlarge/generic_flatlarge.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_flatmedium/generic_flatmedium.audio A=128B B=128B +~ effects/buildingexplosions/generic_flatmedium/generic_flatmedium.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_large/generic_large.audio A=128B B=128B +~ effects/buildingexplosions/generic_large/generic_large.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_large_blast/generic_large_blast.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_medium/generic_medium.audio A=128B B=128B +~ effects/buildingexplosions/generic_medium/generic_medium.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_small/generic_small.audio A=128B B=128B +~ effects/buildingexplosions/generic_small/generic_small.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/generic_tiny/generic_tiny.audio A=128B B=128B +~ effects/buildingexplosions/generic_tiny/generic_tiny.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/geot_generator/geot_generator.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/guard_comissary/guard_comissary.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/helopad/helopad.audio A=128B B=128B +~ effects/buildingexplosions/helopad/helopad.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/large_guardtower/large_guardtower.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/listening_post/listening_post.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/lrm_turret/lrm_turret.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/marsh_helo_station/marsh_helo_station.audio A=128B B=128B +~ effects/buildingexplosions/marsh_helo_station/marsh_helo_station.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/marsh_hover_station/marsh_hover_station.audio A=128B B=128B +~ effects/buildingexplosions/marsh_hover_station/marsh_hover_station.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/marsh_lrm_turret/marsh_lrm_turret.audio A=128B B=128B +~ effects/buildingexplosions/marsh_lrm_turret/marsh_lrm_turret.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/marsh_radar_station/marsh_radar_station.audio A=128B B=128B +~ effects/buildingexplosions/marsh_radar_station/marsh_radar_station.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/mech_bay/mech_bay.audio A=128B B=128B +~ effects/buildingexplosions/mech_bay/mech_bay.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/mech_hanger/mech_hanger.audio A=128B B=128B +~ effects/buildingexplosions/mech_hanger/mech_hanger.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/mech_repair_bldg/mech_repair_bldg.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/ml_cannon/ml_cannon.audio A=128B B=128B +~ effects/buildingexplosions/ml_cannon/ml_cannon.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/palace_mausoleum/palace_mausoleum.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/prisoner_barracks/prisoner_barracks.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/radiotower/radiotower.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/repair_platform/repair_platform.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/satelite_control/satelite_control.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/shilone_hangar/shilone_hangar.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/tent1/tent1.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/tent2/tent2.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/twinmechbay/twinmechbay.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/vehicle_hangar/vehicle_hangar.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/vehicle_hangar2/vehicle_hangar2.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/vehicle_trailer/vehicle_trailer.data{gamemodel} A=120B B=120B +~ effects/buildingexplosions/weapon_warehouse/weapon_warehouse.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/hrothgar_destruction.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/hrothgar_launch_ground.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/hrothgar_launch_ship.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/hrothgar_predestruction.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/overlord_destruction.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/overlord_launch_ground.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/overlord_launch_ship.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/overlord_predestruction.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/talon_destruction.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/talon_launch_ground.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/talon_launch_ship.data{gamemodel} A=120B B=120B +~ effects/dropship_effects/talon_predestruction.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/bldg_flare.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/burning_wreck.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lavabubble.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunar_transmit.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunar_transmit_blue.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunar_transmit_green.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunar_transmit_red.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunar_transmit_yellow.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunarenv_bubble_up.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunarenv_crevice_steam.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunarenv_geyser_1.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/lunarenv_geyser_2.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/sand.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/sparkles_ground.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/sparkles_large.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/sparkles_medium.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/surface_drill_effect.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/vent_steam.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/vent_steam_grey.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/volcanic_crack_smoke.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/volcanic_debris.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/volcanic_smoke.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/volcanic_smoke_small.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/waterfall.data{gamemodel} A=120B B=120B +~ effects/environmental_effects/waterfall_splash.data{gamemodel} A=120B B=120B +~ effects/hovergroundtrail/hover_trail_brown.data{gamemodel} A=120B B=120B +~ effects/hovergroundtrail/hover_trail_darkbrown.data{gamemodel} A=120B B=120B +~ effects/hovergroundtrail/hover_trail_darkgrey.data{gamemodel} A=120B B=120B +~ effects/hovergroundtrail/hover_trail_grey.data{gamemodel} A=120B B=120B +~ effects/hovergroundtrail/hover_trail_water.data{gamemodel} A=120B B=120B +~ effects/hovergroundtrail/hovergroundtrail.data{gamemodel} A=120B B=120B +~ effects/leaveseffect/leaveseffect.data{gamemodel} A=120B B=120B +~ effects/lightningeffect/lightningeffect.audio A=128B B=128B +~ effects/lightningeffect/lightningeffect.data{gamemodel} A=120B B=120B +~ effects/materialeffectholders/hover_trail.contents A=100B B=100B +~ effects/motobike_trail/motobiketrail.audio A=128B B=128B +~ effects/motobike_trail/motobiketrail.data{gamemodel} A=120B B=120B +~ effects/possumeffect/possumeffect.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_minerl_large01/rock_minerl.audio A=128B B=128B +~ effects/rock_destructions/rock_minerl_large01/rock_minerl_large01.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_minerl_large02/rock_minerl_large02.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_minerl_large03/rock_minerl_large03.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_minerl_medium01/rock_minerl_medium01.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_minerl_medium02/rock_minerl_medium02.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_minerl_medium03/rock_minerl_medium03.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_minerl_small/rock_minerl.audio A=128B B=128B +~ effects/rock_destructions/rock_minerl_small/rock_minerl_small.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_volcan_large01/rock_volcan_large01.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_volcan_large02/rock_volcan_large02.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_volcan_large03/rock_volcan_large03.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_volcan_medium01/rock_volcan_medium01.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_volcan_medium02/rock_volcan_medium02.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_volcan_medium03/rock_volcan_medium03.data{gamemodel} A=120B B=120B +~ effects/rock_destructions/rock_volcan_small/rock_volcan_small.data{gamemodel} A=120B B=120B +~ effects/tank_trail/tank_trail_brown.data{gamemodel} A=120B B=120B +~ effects/tank_trail/tank_trail_darkbrown.data{gamemodel} A=120B B=120B +~ effects/tank_trail/tank_trail_darkgrey.data{gamemodel} A=120B B=120B +~ effects/tank_trail/tank_trail_grey.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/cactus01/cactus01_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/cactus01/cactus01_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/cactus02/cactus02_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/cactus02/cactus02_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/cactus03/cactus03_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/cactus03/cactus03_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/evergreen01/evergreen01_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/evergreen01/evergreen01_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/evergreen02/evergreen02_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/evergreen02/evergreen02_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/fir01/fir01_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/fir01/fir01_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/fir02/fir02_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/fir02/fir02_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/joshua01/joshua01_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/joshua01/joshua01_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/joshua02/joshua02_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/joshua02/joshua02_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/joshua03/joshua03_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/joshua03/joshua03_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/mossdraped02/mossdraped02_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/mossdraped02/mossdraped02_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/multi01/multi01_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/pine01/pine01_death.audio A=128B B=128B +~ effects/treeexplosions/pine01/pine01_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/pine01/pine01_shot.audio A=128B B=128B +~ effects/treeexplosions/pine01/pine01_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/pine02/pine02_death.audio A=128B B=128B +~ effects/treeexplosions/pine02/pine02_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/pine02/pine02_shot.audio A=128B B=128B +~ effects/treeexplosions/pine02/pine02_shot.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/sequoia01/sequoia01_death.audio A=128B B=128B +~ effects/treeexplosions/sequoia01/sequoia01_death.data{gamemodel} A=120B B=120B +~ effects/treeexplosions/sequoia01/sequoia01_shot.audio A=128B B=128B +~ effects/treeexplosions/sequoia02/sequoia02_death.audio A=128B B=128B +~ effects/treeexplosions/sequoia02/sequoia02_shot.audio A=128B B=128B +~ effects/treeexplosions/streetlight_death/streetlight_death.audio A=128B B=128B +~ effects/treeexplosions/test_shot/test_shot.audio A=128B B=128B +~ effects/vehicle_destruction/peregrine/peregrine.audio A=128B B=128B +~ interfaces/common/chiinterface.control A=5646B B=5646B +~ interfaces/common/daninterface.control A=5646B B=5646B +~ interfaces/common/fininterface.control A=5646B B=5646B +~ interfaces/common/freinterface.control A=5646B B=5646B +~ interfaces/common/gerinterface.control A=5646B B=5646B +~ interfaces/common/interface.control A=5970B B=5970B +~ interfaces/common/itainterface.control A=5646B B=5646B +~ interfaces/common/japinterface.control A=5646B B=5646B +~ interfaces/common/korinterface.control A=5646B B=5646B +~ interfaces/common/norinterface.control A=5646B B=5646B +~ interfaces/common/porinterface.control A=5646B B=5646B +~ interfaces/common/spainterface.control A=5646B B=5646B +~ interfaces/common/sweinterface.control A=5646B B=5646B +~ misc/flag_team0/flag_team0.data{gamemodel} A=664B B=664B +~ misc/flag_team1/flag_team1.data{gamemodel} A=664B B=664B +~ misc/flag_team1/flag_team1.instance A=308B B=308B +~ misc/flag_team2/flag_team2.data{gamemodel} A=664B B=664B +~ misc/flag_team3/flag_team3.data{gamemodel} A=664B B=664B +~ misc/flag_team3/flag_team3.instance A=308B B=308B +~ misc/flag_team4/flag_team4.data{gamemodel} A=664B B=664B +~ misc/flag_team5/flag_team5.data{gamemodel} A=664B B=664B +~ misc/flag_team5/flag_team5.instance A=308B B=308B +~ misc/flag_team6/flag_team6.data{gamemodel} A=664B B=664B +~ misc/flag_team7/flag_team7.data{gamemodel} A=664B B=664B +~ misc/flag_team7/flag_team7.instance A=308B B=308B +~ misc/flag_team8/flag_team8.data{gamemodel} A=664B B=664B +~ misc/navpoint/navpoint.data{gamemodel} A=664B B=664B +~ misc/navpoint/navpoint.instance A=308B B=308B +~ misc/objective/objective.instance A=1148B B=1148B +~ misc/team/team.data{gamemodel} A=756B B=756B +~ misc/team/team.instance A=308B B=308B +~ shellscripts/computerplayer.script A=6233B B=6226B +~ shellscripts/conlobby.script A=141044B B=140532B +~ shellscripts/credits.script A=8684B B=8614B +~ shellscripts/graphics/multiplayer/lobbyskins/skinc4.tga A=3643B B=3090B +~ shellscripts/graphics/multiplayer/lobbyskins/skinu4.tga A=3642B B=3116B +~ shellscripts/listboxes.script A=30785B B=29269B +~ shellscripts/mainmenu.script A=15247B B=15212B +~ shellscripts/mc_listboxes.script A=58773B B=58767B +~ shellscripts/mc_listboxes_controls.script A=52698B B=52692B +~ shellscripts/mechbay/advancetime.script A=12677B B=12671B +~ shellscripts/mechbay/armor.script A=64539B B=61807B +~ shellscripts/mechbay/chassis.script A=82596B B=81881B +~ shellscripts/mechbay/graphics/installed/weapon_-1_1_0.tga A=1036B B=6540B +~ shellscripts/mechbay/graphics/installed/weapon_-1_1_1.tga A=1036B B=6540B +~ shellscripts/mechbay/graphics/installed/weapon_-1_1_2.tga A=1036B B=6540B +~ shellscripts/mechbay/graphics/installed/weapon_-1_1_3.tga A=1036B B=6540B +~ shellscripts/mechbay/graphics/installed/weapon_-1_2_0.tga A=1114B B=13036B +~ shellscripts/mechbay/graphics/installed/weapon_-1_2_1.tga A=1114B B=13036B +~ shellscripts/mechbay/graphics/installed/weapon_-1_2_2.tga A=1114B B=13036B +~ shellscripts/mechbay/graphics/installed/weapon_-1_2_3.tga A=1114B B=13036B +~ shellscripts/mechbay/graphics/installed/weapon_-1_3_0.tga A=1357B B=19532B +~ shellscripts/mechbay/graphics/installed/weapon_-1_3_1.tga A=1357B B=19532B +~ shellscripts/mechbay/graphics/installed/weapon_-1_3_2.tga A=1357B B=19532B +~ shellscripts/mechbay/graphics/installed/weapon_-1_3_3.tga A=1357B B=19532B +~ shellscripts/mechbay/graphics/installed/weapon_-1_4_0.tga A=1620B B=26028B +~ shellscripts/mechbay/graphics/installed/weapon_-1_4_1.tga A=1620B B=26028B +~ shellscripts/mechbay/graphics/installed/weapon_-1_4_2.tga A=1620B B=26028B +~ shellscripts/mechbay/graphics/installed/weapon_-1_4_3.tga A=1620B B=26028B +~ shellscripts/mechbay/graphics/installed/weapon_-1_5_0.tga A=1843B B=32524B +~ shellscripts/mechbay/graphics/installed/weapon_-1_5_1.tga A=1843B B=32524B +~ shellscripts/mechbay/graphics/installed/weapon_-1_5_2.tga A=1843B B=32524B +~ shellscripts/mechbay/graphics/installed/weapon_-1_5_3.tga A=1843B B=32524B +~ shellscripts/mechbay/graphics/installed/weapon_-1_6_0.tga A=2096B B=39020B +~ shellscripts/mechbay/graphics/installed/weapon_-1_6_1.tga A=2096B B=39020B +~ shellscripts/mechbay/graphics/installed/weapon_-1_6_2.tga A=2096B B=39020B +~ shellscripts/mechbay/graphics/installed/weapon_-1_6_3.tga A=2096B B=39020B +~ shellscripts/mechbay/gs_chassis.script A=34465B B=34389B +~ shellscripts/mechbay/gs_mechbay_main.script A=14412B B=14406B +~ shellscripts/mechbay/infobox.script A=43875B B=41595B +~ shellscripts/mechbay/mc_listboxes_weapon.script A=47284B B=47278B +~ shellscripts/mechbay/mechbay.script A=1282B B=1276B +~ shellscripts/mechbay/mechbay_main.script A=94517B B=61432B +~ shellscripts/mechbay/weapons.script A=102025B B=83653B +~ shellscripts/mechlabheaders.h A=9142B B=8994B +~ shellscripts/mechweight.script A=5636B B=5630B +~ shellscripts/multiplayer/buildrestriction.script A=16443B B=15713B +~ shellscripts/multiplayer/con_skins_listbox.script A=28607B B=28601B +~ shellscripts/multiplayer/conlobbymission.script A=60376B B=60362B +~ shellscripts/multiplayer/hostlobbyserver.script A=22524B B=22520B +~ shellscripts/multiplayer/listofgames.script A=25575B B=25571B +~ shellscripts/multiplayer/mc_listboxes.script A=54403B B=54397B +~ shellscripts/multiplayer/mech_var_listbox.script A=28886B B=28845B +~ shellscripts/multiplayer/multicolumnlist.script A=39743B B=39741B +~ shellscripts/multiplayer/multiplayerconsole.script A=16189B B=16187B +~ shellscripts/netlobby.script A=163740B B=163088B +~ shellscripts/scriptstrings.h A=71608B B=70811B +~ shellscripts/stddefs.h A=5313B B=5307B +~ shellscripts/teslaplayer.script A=19744B B=19743B +~ vehicles/ammo_carrier/ammo_carrier.audio A=330B B=330B +~ vehicles/ammo_carrier/ammo_carrier.instance A=308B B=308B +~ vehicles/ammo_carrier_destroyed/ammo_carrier_destroyed.data{gamemodel} A=28B B=28B +~ vehicles/apc/apc.audio A=330B B=330B +~ vehicles/apc/apc.contents A=282B B=282B +~ vehicles/apc/apc.instance A=308B B=308B +~ vehicles/apc/apc.subsystems A=598B B=598B +~ vehicles/apc/apc.torso{gamemodel} A=1608B B=1608B +~ vehicles/apc/armaturedata/hull.data{gamemodel} A=80B B=80B +~ vehicles/apc_destroyed/apc_destroyed.data{gamemodel} A=28B B=28B +~ vehicles/artillery_launcher/artillery_launcher.audio A=330B B=330B +~ vehicles/artillery_launcher/artillery_launcher.torso{gamemodel} A=1608B B=1608B +~ vehicles/boxvan_destroyed/boxvan_destroyed.data{gamemodel} A=28B B=28B +~ vehicles/bulldog/bulldog.contents[joint_hull]{armature} A=282B B=282B +~ vehicles/bulldog/bulldog.contents[joint_twist]{armature} A=282B B=282B +~ vehicles/bulldog/bulldog.subsystems A=1358B B=1358B +~ vehicles/bulldog/bulldog.torso{gamemodel} A=1608B B=1608B +~ vehicles/bus/bus.audio A=330B B=330B +~ vehicles/bus_destroyed/bus_destroyed.data{gamemodel} A=28B B=28B +~ vehicles/car/car.audio A=330B B=330B +~ vehicles/car/car.instance A=308B B=308B +~ vehicles/cargotrack_destroyed/cargotrack_destroyed.data{gamemodel} A=28B B=28B +~ vehicles/condor/condor.contents[joint_hull]{armature} A=282B B=282B +~ vehicles/condor/condor.subsystems A=1738B B=1738B +~ vehicles/condor/condor.torso{gamemodel} A=1608B B=1608B +~ vehicles/destroyer1/destroyer1.contents A=282B B=282B +~ vehicles/destroyer1/destroyer1.instance A=308B B=308B +~ vehicles/destroyer1/destroyer1.subsystems A=2714B B=2714B +~ vehicles/field_base/field_base.contents[joint_base]{armature} A=1682B B=1682B +~ vehicles/field_base/field_base.data{gamemodel} A=776B B=776B +~ vehicles/field_base/field_base.instance A=308B B=308B +~ vehicles/field_base_destroyed/field_base_destroyed.data{gamemodel} A=28B B=28B +~ vehicles/firetruck/firetruck.subsystems A=1298B B=1298B +~ vehicles/firetruck/firetruck.torso{gamemodel} A=1608B B=1608B +~ vehicles/harasser/harasser.subsystems A=1358B B=1358B +~ vehicles/harasser/harasser.torso{gamemodel} A=1608B B=1608B +~ vehicles/hrothgar/gun1.torso{gamemodel} A=1608B B=1608B +~ vehicles/hrothgar/gun2.torso{gamemodel} A=1608B B=1608B +~ vehicles/hrothgar/gun3.torso{gamemodel} A=1608B B=1608B +~ vehicles/hrothgar/gun4.torso{gamemodel} A=1608B B=1608B +~ vehicles/hrothgar/hrothgar.contents[joint_engines]{armature} A=1122B B=1122B +~ vehicles/hrothgar/hrothgar.subsystems A=8522B B=8522B +~ vehicles/humvee/humvee.torso{gamemodel} A=1608B B=1608B +~ vehicles/karnov/karnov.contents A=282B B=282B +~ vehicles/karnov/karnov.instance A=312B B=312B +~ vehicles/karnov/karnov.subsystems A=870B B=870B +~ vehicles/loader/loader.instance A=308B B=308B +~ vehicles/lrm_carrier/lrm_carrier.audio A=330B B=330B +~ vehicles/lrm_carrier/lrm_carrier.subsystems A=1358B B=1358B +~ vehicles/magi_tank/magi_tank.contents[joint_hull]{armature} A=282B B=282B +~ vehicles/magi_tank/magi_tank.subsystems A=1738B B=1738B +~ vehicles/magi_tank_destroyed/magi_tank_destroyed.data{gamemodel} A=28B B=28B +~ vehicles/mobile_apu/mobile_apu.instance A=308B B=308B +~ vehicles/mobile_turret_control/mobile_turret_control.contents A=282B B=282B +~ vehicles/mobile_turret_control/mobile_turret_control.instance A=308B B=308B +~ vehicles/mobile_turret_control/mobile_turret_control.subsystems A=218B B=218B +~ vehicles/mobile_turret_control/mobile_turret_control.torso{gamemodel} A=1608B B=1608B +~ vehicles/nightshade/nightshade.contents A=282B B=282B +~ vehicles/nightshade/nightshade.instance A=312B B=312B +~ vehicles/overlord/overlord.contents[joint_cannonringparent]{armature} A=282B B=282B +~ vehicles/overlord/overlord.contents[joint_lowergun01parent]{armature} A=282B B=282B +~ vehicles/overlord/overlord.contents[joint_lowergun02parent]{armature} A=282B B=282B +~ vehicles/overlord/overlord.contents[joint_lowergun03parent]{armature} A=282B B=282B +~ vehicles/overlord/overlord.contents[joint_overlord]{armature} A=1962B B=1962B +~ vehicles/overlord/overlord.contents[joint_uppergun01parent]{armature} A=282B B=282B +~ vehicles/overlord/overlord.contents[joint_uppergun02parent]{armature} A=282B B=282B +~ vehicles/overlord/overlord.contents[joint_uppergun03parent]{armature} A=282B B=282B +~ vehicles/overlord/overlord.subsystems A=4670B B=4670B +~ vehicles/patrolboat1/patrolboat1.torso{gamemodel} A=1608B B=1608B +~ vehicles/patrolboat2/patrolboat2.torso{gamemodel} A=1608B B=1608B +~ vehicles/srm_carrier/srm_carrier.subsystems A=1358B B=1358B +~ vehicles/swiftwind_destroyed/swiftwind_destroyed.data{gamemodel} A=28B B=28B +~ vehicles/talon/armaturedata/logunar.data{gamemodel} A=80B B=80B +~ vehicles/talon/talon.contents[joint_hull]{armature} A=2802B B=2802B +~ vehicles/talon/talon.subsystems A=4122B B=4122B +~ vehicles/talon/talon_lal.torso{gamemodel} A=1608B B=1608B +~ vehicles/talon/talon_lar.torso{gamemodel} A=1608B B=1608B +~ vehicles/talon/talon_lfl.torso{gamemodel} A=1608B B=1608B +~ vehicles/talon/talon_lfr.torso{gamemodel} A=1608B B=1608B +~ vehicles/talon/talon_mid.torso{gamemodel} A=1608B B=1608B +~ vehicles/talon/talon_ual.torso{gamemodel} A=1608B B=1608B +~ vehicles/talon/talon_uar.torso{gamemodel} A=1608B B=1608B +~ vehicles/talon/talon_ufl.torso{gamemodel} A=1608B B=1608B +~ vehicles/talon/talon_ufr.torso{gamemodel} A=1608B B=1608B +~ vehicles/vedette/vedette.subsystems A=978B B=978B +~ vehicles/vedette/vedette.torso{gamemodel} A=1608B B=1608B diff --git a/MW4COMPARE/reports/resource-files-diff.txt b/MW4COMPARE/reports/resource-files-diff.txt new file mode 100644 index 00000000..619974a8 --- /dev/null +++ b/MW4COMPARE/reports/resource-files-diff.txt @@ -0,0 +1,1007 @@ +# V4H=983 files, OURS=280 files | identical=71 differ=115 V4H_only=797 OURS_only=94 ++V4H Variants/Annihilator (Gausszilla).mw4 1995 ++V4H Variants/Annihilator ANH-1A.mw4 2653 ++V4H Variants/Annihilator ANH-1E.mw4 1794 ++V4H Variants/Annihilator ANH-1G.mw4 1333 ++V4H Variants/Annihilator ANH-1X.mw4 2858 ++V4H Variants/Annihilator ANH-2A.mw4 2493 ++V4H Variants/Annihilator ANH-2AX.mw4 1206 ++V4H Variants/Annihilator ANH-3A.mw4 2199 ++V4H Variants/Annihilator ANH-4A.mw4 2510 ++V4H Variants/Annihilator C 2.mw4 1375 ++V4H Variants/Annihilator C.mw4 1667 ++V4H Variants/Archer (Morgan).mw4 2237 ++V4H Variants/Archer (Wolf).mw4 2258 ++V4H Variants/Archer ARC-2K.mw4 2056 ++V4H Variants/Archer ARC-2R.mw4 1743 ++V4H Variants/Archer ARC-2Rb.mw4 1878 ++V4H Variants/Archer ARC-2S.mw4 2347 ++V4H Variants/Archer ARC-2W.mw4 2619 ++V4H Variants/Archer ARC-4M.mw4 1906 ++V4H Variants/Archer ARC-5CS.mw4 1958 ++V4H Variants/Archer ARC-5R.mw4 2369 ++V4H Variants/Archer ARC-5S.mw4 1621 ++V4H Variants/Archer ARC-5W.mw4 1964 ++V4H Variants/Archer ARC-6S.mw4 1962 ++V4H Variants/Archer ARC-7 L.mw4 1806 ++V4H Variants/Archer ARC-7C.mw4 1634 ++V4H Variants/Archer ARC-7S.mw4 1708 ++V4H Variants/Archer ARC-8M.mw4 1708 ++V4H Variants/Archer ARC-9K.mw4 1942 ++V4H Variants/Archer ARC-9M.mw4 1661 ++V4H Variants/Archer ARC-9W.mw4 1809 ++V4H Variants/Archer C.mw4 1752 ++V4H Variants/Arctic Wolf Prime.mw4 1925 ++V4H Variants/Arctic Wolf 1.mw4 1607 ++V4H Variants/Arctic Wolf 2.mw4 2536 ++V4H Variants/Arctic Wolf A.mw4 1736 ++V4H Variants/Arctic Wolf II Prime.mw4 1450 ++V4H Variants/Arctic Wolf II A.mw4 1978 ++V4H Variants/Arctic Wolf II B.mw4 1544 ++V4H Variants/Arctic Wolf II C.mw4 1716 ++V4H Variants/Argus AGS-2D.mw4 1696 ++V4H Variants/Argus AGS-4D.mw4 2636 ++V4H Variants/Argus AGS-5D.mw4 1924 ++V4H Variants/Argus AGS-6F.mw4 1817 ++V4H Variants/AssassinII (Alice).mw4 2053 ++V4H Variants/AssassinII (Servitor).mw4 2042 ++V4H Variants/AssassinII ASN-101.mw4 1670 ++V4H Variants/AssassinII ASN-21.mw4 1280 ++V4H Variants/AssassinII ASN-23.mw4 1212 ++V4H Variants/AssassinII ASN-30.mw4 1484 ++V4H Variants/AssassinII ASN-99.mw4 1777 ++V4H Variants/Atlas (Danielle).mw4 1675 ++V4H Variants/Atlas (Devlin).mw4 1997 ++V4H Variants/Atlas (Jedra).mw4 1651 ++V4H Variants/Atlas (Jurn).mw4 1964 ++V4H Variants/Atlas (Kerensky).mw4 2195 ++V4H Variants/Atlas AS7-A.mw4 1662 ++V4H Variants/Atlas AS7-C.mw4 2432 ++V4H Variants/Atlas AS7-D-DC.mw4 1838 ++V4H Variants/Atlas AS7-D.mw4 2230 ++V4H Variants/Atlas AS7-Dr.mw4 1660 ++V4H Variants/Atlas AS7-K-DC.mw4 1805 ++V4H Variants/Atlas AS7-K.mw4 1913 ++V4H Variants/Atlas AS7-K2.mw4 2584 ++V4H Variants/Atlas AS7-RS.mw4 2226 ++V4H Variants/Atlas AS7-S.mw4 2648 ++V4H Variants/Atlas AS7-S2.mw4 2187 ++V4H Variants/Atlas AS7-S3.mw4 1632 ++V4H Variants/Atlas AS7-WGS.mw4 1304 ++V4H Variants/Atlas AS8-D.mw4 1684 ++V4H Variants/Atlas C.mw4 1401 ++V4H Variants/Atlas II AS7-D-H.mw4 2015 ++V4H Variants/Atlas II AS7-D-H2.mw4 1702 ++V4H Variants/Atlas Pharaoh.mw4 1765 ++V4H Variants/Avatar AV1-O Prime.mw4 2164 ++V4H Variants/Avatar AV1-OA.mw4 2200 ++V4H Variants/Avatar AV1-OB.mw4 1802 ++V4H Variants/Avatar AV1-OC.mw4 2317 ++V4H Variants/Avatar AV1-OD.mw4 2670 ++V4H Variants/Avatar AV1-OE.mw4 2253 ++V4H Variants/Avatar AV1-OF.mw4 2382 ++V4H Variants/Avatar AV1-OG.mw4 1423 ++V4H Variants/Avatar AV1-OI.mw4 1617 ++V4H Variants/Avatar AV1-OR.mw4 1901 ++V4H Variants/Avatar Avatar 4.10.mw4 3012 ++V4H Variants/Avatar Satyr 4.10.mw4 1971 ++V4H Variants/Awesome (Buck).mw4 1598 ++V4H Variants/Awesome (Cameron).mw4 1862 ++V4H Variants/Awesome (Klatt).mw4 1751 ++V4H Variants/Awesome AWS-10KM.mw4 1746 ++V4H Variants/Awesome AWS-11M.mw4 2062 ++V4H Variants/Awesome AWS-8Q.mw4 1661 ++V4H Variants/Awesome AWS-8R.mw4 1661 ++V4H Variants/Awesome AWS-8T.mw4 1972 ++V4H Variants/Awesome AWS-8V.mw4 1771 ++V4H Variants/Awesome AWS-9M.mw4 2062 ++V4H Variants/Awesome AWS-9Ma.mw4 1781 ++V4H Variants/Awesome AWS-9Q.mw4 1787 ++V4H Variants/Battlemaster (Calvin 2).mw4 2029 ++V4H Variants/Battlemaster (Calvin).mw4 1902 ++V4H Variants/Battlemaster (Red Corsair).mw4 1889 ++V4H Variants/Battlemaster (Rogers).mw4 2045 ++V4H Variants/Battlemaster BLR-10S.mw4 2038 ++V4H Variants/Battlemaster BLR-10S2.mw4 2499 ++V4H Variants/Battlemaster BLR-1D.mw4 1879 ++V4H Variants/Battlemaster BLR-1G-DC.mw4 2266 ++V4H Variants/Battlemaster BLR-1G.mw4 1786 ++V4H Variants/Battlemaster BLR-1Gb.mw4 2063 ++V4H Variants/Battlemaster BLR-1Gbc.mw4 1930 ++V4H Variants/Battlemaster BLR-1Gc.mw4 1942 ++V4H Variants/Battlemaster BLR-1S.mw4 2097 ++V4H Variants/Battlemaster BLR-2C.mw4 2346 ++V4H Variants/Battlemaster BLR-3M.mw4 3272 ++V4H Variants/Battlemaster BLR-3S.mw4 2280 ++V4H Variants/Battlemaster BLR-4S.mw4 3154 ++V4H Variants/Battlemaster BLR-5M.mw4 2628 ++V4H Variants/Battlemaster BLR-6C.mw4 2016 ++V4H Variants/Battlemaster BLR-6X.mw4 1648 ++V4H Variants/Battlemaster IC Garg Prime.mw4 2548 ++V4H Variants/Battlemaster IC Garg (Conal).mw4 1638 ++V4H Variants/Battlemaster IC Garg A.mw4 1630 ++V4H Variants/Battlemaster IC Garg B.mw4 1527 ++V4H Variants/Battlemaster IC Garg C.mw4 1778 ++V4H Variants/Battlemaster IC Garg D.mw4 1965 ++V4H Variants/Battlemaster IC Garg E.mw4 2050 ++V4H Variants/Battlemaster IC Garg G.mw4 1744 ++V4H Variants/Battlemaster IC Garg H.mw4 1698 ++V4H Variants/Battlemaster Stock Test.mw4 1939 ++V4H Variants/BattlemasterIIC C3.mw4 2081 ++V4H Variants/BattlemasterIIC Frost.mw4 2181 ++V4H Variants/BattlemasterIIC Stock Test.mw4 1952 ++V4H Variants/Behemoth 1.mw4 1489 ++V4H Variants/Behemoth 2.mw4 2087 ++V4H Variants/Behemoth I 3.mw4 2038 ++V4H Variants/Behemoth I 4.mw4 2372 ++V4H Variants/Behemoth I 5.mw4 1789 ++V4H Variants/Behemoth I 6.mw4 2237 ++V4H Variants/Behemoth I 7.mw4 2062 ++V4H Variants/BehemothII Redgrave.mw4 1869 ++V4H Variants/Black Hawk BHKU-O Prime.mw4 2595 ++V4H Variants/Black Hawk BHKU-OA.mw4 1484 ++V4H Variants/Black Hawk BHKU-OB.mw4 1895 ++V4H Variants/Black Hawk BHKU-OC.mw4 1553 ++V4H Variants/Black Hawk BHKU-OD.mw4 1491 ++V4H Variants/Black Hawk BHKU-OE.mw4 3408 ++V4H Variants/Black Hawk BHKU-OF.mw4 1585 ++V4H Variants/Black Hawk BHKU-OG.mw4 1470 ++V4H Variants/Black Hawk BHKU-OR.mw4 1469 ++V4H Variants/Black Hawk BHKU-OX.mw4 2085 ++V4H Variants/Black Hawk Prime.mw4 2661 ++V4H Variants/Black Hawk A.mw4 1578 ++V4H Variants/Black Hawk B.mw4 1840 ++V4H Variants/Black Hawk Black Hawk 4.10.mw4 2458 ++V4H Variants/Black Hawk C.mw4 1429 ++V4H Variants/Black Hawk D.mw4 1376 ++V4H Variants/Black Hawk E.mw4 1736 ++V4H Variants/Black Hawk H.mw4 2069 ++V4H Variants/Black Hawk I.mw4 1734 ++V4H Variants/Black Hawk Nova 4.10.mw4 2328 ++V4H Variants/Black Hawk S.mw4 2157 ++V4H Variants/Black Hawk Standard 1.mw4 1816 ++V4H Variants/Black Hawk Standard 2.mw4 1521 ++V4H Variants/Black Hawk Standard 3.mw4 1768 ++V4H Variants/Black Knight (Ross).mw4 1844 ++V4H Variants/Black Knight BL-12-KNT.mw4 2305 ++V4H Variants/Black Knight BL-6-KNT.mw4 1802 ++V4H Variants/Black Knight BL-6-RR.mw4 2261 ++V4H Variants/Black Knight BL-6b-KNT.mw4 2049 ++V4H Variants/Black Knight BL-7-KNT-L.mw4 1881 ++V4H Variants/Black Knight BL-7-KNT.mw4 1894 ++V4H Variants/Black Knight BL-9-KNT.mw4 2281 ++V4H Variants/Black Lanner Prime.mw4 2413 ++V4H Variants/Black Lanner A.mw4 1941 ++V4H Variants/Black Lanner B.mw4 1539 ++V4H Variants/Black Lanner C.mw4 1882 ++V4H Variants/Black Lanner D.mw4 2364 ++V4H Variants/Black Lanner Duck.mw4 3416 ++V4H Variants/Black Lanner E.mw4 1820 ++V4H Variants/Black Lanner H.mw4 1670 ++V4H Variants/Black Lanner X.mw4 1330 ++V4H Variants/Brigand LDT-1.mw4 1395 ++V4H Variants/Brigand LDT-X1.mw4 1839 ++V4H Variants/Brigand LDT-X3.mw4 1416 ++V4H Variants/Brigand LDT-X4.mw4 2236 ++V4H Variants/Brigand LDT-XPR1.mw4 1696 ++V4H Variants/Brigand LDT-XPR2.mw4 1875 ++V4H Variants/Bushwacker BSW-L1.mw4 1405 ++V4H Variants/Bushwacker BSW-S2.mw4 2229 ++V4H Variants/Bushwacker BSW-S2r.mw4 1403 ++V4H Variants/Bushwacker BSW-X1.mw4 1819 ++V4H Variants/Bushwacker BSW-X2.mw4 1908 ++V4H Variants/Bushwacker Wildonion.mw4 1555 ++V4H Variants/Catapult (Butterbee).mw4 2072 ++V4H Variants/Catapult CPLT-A1.mw4 1500 ++V4H Variants/Catapult CPLT-C1.mw4 2437 ++V4H Variants/Catapult CPLT-C1b.mw4 1937 ++V4H Variants/Catapult CPLT-C2.mw4 1695 ++V4H Variants/Catapult CPLT-C3.mw4 1770 ++V4H Variants/Catapult CPLT-C4.mw4 1679 ++V4H Variants/Catapult CPLT-C4C.mw4 2307 ++V4H Variants/Catapult CPLT-C5.mw4 1864 ++V4H Variants/Catapult CPLT-C5A.mw4 2013 ++V4H Variants/Catapult CPLT-C6.mw4 1513 ++V4H Variants/Catapult CPLT-K 5.mw4 2054 ++V4H Variants/Catapult CPLT-K2.mw4 2493 ++V4H Variants/Catapult CPLT-K2K.mw4 1606 ++V4H Variants/Catapult CPLT-K3.mw4 1679 ++V4H Variants/Catapult CPLT-K4.mw4 1697 ++V4H Variants/Catapult Duck.mw4 1892 ++V4H Variants/Cauldron-Born B.mw4 2390 ++V4H Variants/Cauldron-Born Prime.mw4 2302 ++V4H Variants/Cauldron-Born (Samantha).mw4 1771 ++V4H Variants/Cauldron-Born A.mw4 2179 ++V4H Variants/Cauldron-Born C.mw4 2695 ++V4H Variants/Cauldron-Born D.mw4 2488 ++V4H Variants/Cauldron-Born H.mw4 2139 ++V4H Variants/Cauldron-Born Redgrave.mw4 1930 ++V4H Variants/Champion C.mw4 1724 ++V4H Variants/Champion CHP-1 N 2.mw4 1546 ++V4H Variants/Champion CHP1N.mw4 1577 ++V4H Variants/Champion CHP2N.mw4 1566 ++V4H Variants/Champion CHP3N.mw4 1616 ++V4H Variants/Chimera CMA-1S.mw4 2041 ++V4H Variants/Chimera CMA-C.mw4 1436 ++V4H Variants/Commando (Freyr).mw4 1977 ++V4H Variants/Commando COM-1B.mw4 1307 ++V4H Variants/Commando COM-1C.mw4 1109 ++V4H Variants/Commando COM-1D.mw4 1182 ++V4H Variants/Commando COM-2D.mw4 1302 ++V4H Variants/Commando COM-2Dr.mw4 1097 ++V4H Variants/Commando COM-3A.mw4 1365 ++V4H Variants/Commando COM-5S.mw4 1234 ++V4H Variants/Commando COM-7 S.mw4 1859 ++V4H Variants/Commando COM-7B.mw4 2172 ++V4H Variants/Commando Frost.mw4 2684 ++V4H Variants/Commando IIC.mw4 1762 ++V4H Variants/Commando Pyrotech.mw4 1593 ++V4H Variants/Cougar Prime.mw4 1769 ++V4H Variants/Cougar A.mw4 1737 ++V4H Variants/Cougar B.mw4 2113 ++V4H Variants/Cougar C.mw4 2345 ++V4H Variants/Cougar D.mw4 1857 ++V4H Variants/Cougar E.mw4 2115 ++V4H Variants/Cougar F.mw4 2402 ++V4H Variants/Cougar H.mw4 1563 ++V4H Variants/Cougar XR.mw4 1980 ++V4H Variants/Cyclops CP-10-Q.mw4 1594 ++V4H Variants/Cyclops CP-10-Z.mw4 1619 ++V4H Variants/Cyclops CP-11-A-DC.mw4 2091 ++V4H Variants/Cyclops CP-11-A.mw4 2111 ++V4H Variants/Cyclops CP-11-B.mw4 1519 ++V4H Variants/Cyclops CP-11-G.mw4 2229 ++V4H Variants/Cyclops CP-12-K.mw4 2109 ++V4H Variants/Daishi Prime.mw4 2362 ++V4H Variants/Daishi (Hohiro).mw4 2124 ++V4H Variants/Daishi (Prometheus).mw4 3134 ++V4H Variants/Daishi (Widowmaker).mw4 2153 ++V4H Variants/Daishi A.mw4 1933 ++V4H Variants/Daishi B.mw4 2232 ++V4H Variants/Daishi Cliffjumper.mw4 1799 ++V4H Variants/Daishi Duck.mw4 2093 ++V4H Variants/Daishi H.mw4 3080 ++V4H Variants/Daishi W.mw4 1888 ++V4H Variants/Daishi X.mw4 1975 ++V4H Variants/Dasher Prime.mw4 1965 ++V4H Variants/Dasher (Aletha).mw4 1472 ++V4H Variants/Dasher A.mw4 1243 ++V4H Variants/Dasher B.mw4 1594 ++V4H Variants/Dasher C.mw4 1181 ++V4H Variants/Dasher D.mw4 2301 ++V4H Variants/Dasher E.mw4 1117 ++V4H Variants/Dasher H.mw4 1484 ++V4H Variants/Dasher I.mw4 1630 ++V4H Variants/Dasher M.mw4 1629 ++V4H Variants/Deimos Prime.mw4 1801 ++V4H Variants/Deimos 2.mw4 1771 ++V4H Variants/Deimos A.mw4 1826 ++V4H Variants/Deimos B.mw4 1878 ++V4H Variants/Deimos E.mw4 2596 ++V4H Variants/Dragon (Douglas).mw4 1369 ++V4H Variants/Dragon (Mark).mw4 1926 ++V4H Variants/Dragon (Yoriyoshi).mw4 2285 ++V4H Variants/Dragon DRG-1C.mw4 1338 ++V4H Variants/Dragon DRG-1N.mw4 1612 ++V4H Variants/Dragon DRG-5N.mw4 1226 ++V4H Variants/Dragon DRG-5Nr.mw4 2156 ++V4H Variants/Dragon DRG-7N.mw4 2097 ++V4H Variants/Dragon GD DRG-1G.mw4 1598 ++V4H Variants/Dragon GD DRG-5K-DC.mw4 2650 ++V4H Variants/Dragon GD DRG-5K.mw4 1883 ++V4H Variants/Dragon GD DRG-7K.mw4 2260 ++V4H Variants/Dragon GD DRG-7KC.mw4 1802 ++V4H Variants/Dragon GD DRG-9KC.mw4 1764 ++V4H Variants/Dragon GD DRG-C.mw4 1627 ++V4H Variants/Dragon II .mw4 1658 ++V4H Variants/Dragon II 2.mw4 1439 ++V4H Variants/Fafnir FNR-5.mw4 1631 ++V4H Variants/Fafnir FNR-5B.mw4 3388 ++V4H Variants/Fafnir FNR-5X.mw4 1614 ++V4H Variants/Fafnir FNR-6U.mw4 1662 ++V4H Variants/Flea (Fire Ant).mw4 1999 ++V4H Variants/Flea FLE-15.mw4 1473 ++V4H Variants/Flea FLE-16.mw4 1697 ++V4H Variants/Flea FLE-17.mw4 2197 ++V4H Variants/Flea FLE-20.mw4 2046 ++V4H Variants/Flea FLE-4.mw4 1295 ++V4H Variants/Flea Marvin.mw4 1688 ++V4H Variants/Gladiator Prime.mw4 1824 ++V4H Variants/Gladiator A.mw4 2034 ++V4H Variants/Gladiator B.mw4 2008 ++V4H Variants/Gladiator C.mw4 2170 ++V4H Variants/Gladiator D.mw4 2234 ++V4H Variants/Gladiator E.mw4 1691 ++V4H Variants/Gladiator F.mw4 1814 ++V4H Variants/Gladiator H.mw4 1776 ++V4H Variants/Gladiator I.mw4 1838 ++V4H Variants/Gladiator T.mw4 1617 ++V4H Variants/Gladiator TC.mw4 1908 ++V4H Variants/Griffin (Francine II).mw4 1945 ++V4H Variants/Griffin (Francine).mw4 1617 ++V4H Variants/Griffin GRF-1DS.mw4 1630 ++V4H Variants/Griffin GRF-1E (Sparky).mw4 1643 ++V4H Variants/Griffin GRF-1E2 (Sparky2.mw4 1788 ++V4H Variants/Griffin GRF-1N.mw4 1187 ++V4H Variants/Griffin GRF-1S.mw4 1482 ++V4H Variants/Griffin GRF-2N.mw4 1723 ++V4H Variants/Griffin GRF-3M.mw4 1843 ++V4H Variants/Griffin GRF-5M.mw4 1454 ++V4H Variants/Griffin GRF-6S.mw4 1622 ++V4H Variants/Griffin IIC 2.mw4 1740 ++V4H Variants/Griffin IIC 3.mw4 1741 ++V4H Variants/Griffin IIC 4.mw4 1485 ++V4H Variants/Griffin IIC.mw4 1823 ++V4H Variants/Grizzly 1.mw4 2420 ++V4H Variants/Grizzly 2.mw4 1837 ++V4H Variants/Hauptmann HA1-O.mw4 3022 ++V4H Variants/Hauptmann HA1-OA.mw4 3050 ++V4H Variants/Hauptmann HA1-OB.mw4 2629 ++V4H Variants/Hauptmann HA1-OC.mw4 2535 ++V4H Variants/Hauptmann HA1-OE.mw4 1910 ++V4H Variants/Hauptmann HA1-OF.mw4 1627 ++V4H Variants/Hauptmann Sumo.mw4 2192 ++V4H Variants/Hellhound 1.mw4 2139 ++V4H Variants/Hellhound 2.mw4 1575 ++V4H Variants/Hellhound 3.mw4 1496 ++V4H Variants/Hellhound 4.mw4 1839 ++V4H Variants/Hellhound 5.mw4 1659 ++V4H Variants/Hellhound 7.mw4 1553 ++V4H Variants/Hellhound Pyrotech.mw4 2486 ++V4H Variants/Hellhound Shyumbrion.mw4 1799 ++V4H Variants/Hellspawn HSN-10G.mw4 1730 ++V4H Variants/Hellspawn HSN-10SR.mw4 2357 ++V4H Variants/Hellspawn HSN-7D.mw4 2368 ++V4H Variants/Hellspawn HSN-8E.mw4 1941 ++V4H Variants/Hellspawn HSN-9F.mw4 1700 ++V4H Variants/Highlander (Colleen).mw4 1966 ++V4H Variants/Highlander (Jorgensson).mw4 2347 ++V4H Variants/Highlander HGN-641-X-2.mw4 1602 ++V4H Variants/Highlander HGN-694.mw4 1862 ++V4H Variants/Highlander HGN-732.mw4 1797 ++V4H Variants/Highlander HGN-732b.mw4 1843 ++V4H Variants/Highlander HGN-733.mw4 1649 ++V4H Variants/Highlander HGN-733C.mw4 1205 ++V4H Variants/Highlander HGN-733P.mw4 1739 ++V4H Variants/Highlander HGN-734.mw4 2166 ++V4H Variants/Highlander HGN-736.mw4 1754 ++V4H Variants/Highlander HGN-738.mw4 1900 ++V4H Variants/Highlander IIC.mw4 2334 ++V4H Variants/HollanderII BZK-F3.mw4 1191 ++V4H Variants/HollanderII BZK-G1.mw4 1424 ++V4H Variants/HollanderII II BZK-F5.mw4 1566 ++V4H Variants/HollanderII II BZK-F7.mw4 994 ++V4H Variants/HollanderII III BZK-D1.mw4 1403 ++V4H Variants/HollanderII III BZK-D2.mw4 1194 ++V4H Variants/HollanderII III BZK-D3.mw4 1542 ++V4H Variants/HollanderII Plainsrider.mw4 2928 ++V4H Variants/Hunchback (Hohiro).mw4 1702 ++V4H Variants/Hunchback HBK-4G.mw4 1520 ++V4H Variants/Hunchback HBK-4H.mw4 2383 ++V4H Variants/Hunchback HBK-4J.mw4 1873 ++V4H Variants/Hunchback HBK-4N.mw4 1658 ++V4H Variants/Hunchback HBK-4P.mw4 2008 ++V4H Variants/Hunchback HBK-4SP.mw4 2432 ++V4H Variants/Hunchback HBK-5M.mw4 1899 ++V4H Variants/Hunchback HBK-5N.mw4 2230 ++V4H Variants/Hunchback HBK-5P.mw4 2906 ++V4H Variants/Hunchback HBK-5S.mw4 1842 ++V4H Variants/Hunchback HBK-5SG.mw4 1338 ++V4H Variants/Hunchback HBK-5SS.mw4 1705 ++V4H Variants/Hunchback HBK-6N.mw4 1607 ++V4H Variants/Hunchback HBK-6S.mw4 1687 ++V4H Variants/Hunchback HBK-7R.mw4 1683 ++V4H Variants/Hunchback HBK-7S.mw4 1778 ++V4H Variants/Hunchback HBK-7X-4.mw4 1750 ++V4H Variants/Hunchback IIC 2.mw4 1787 ++V4H Variants/Hunchback IIC 3.mw4 1468 ++V4H Variants/Hunchback IIC.mw4 1954 ++V4H Variants/Jenner2c 1.mw4 1541 ++V4H Variants/Jenner2c 4.mw4 1647 ++V4H Variants/Jenner2c Pharaoh.mw4 1688 ++V4H Variants/Kodiak (Cale).mw4 2797 ++V4H Variants/Kodiak 1.mw4 2296 ++V4H Variants/Kodiak 2.mw4 2079 ++V4H Variants/Kodiak 3.mw4 2098 ++V4H Variants/Kodiak 4.mw4 1820 ++V4H Variants/Kodiak 5.mw4 2258 ++V4H Variants/Kodiak Hard Ass.mw4 1908 ++V4H Variants/Kodiak II.mw4 2937 ++V4H Variants/Kodiak Kiki.mw4 2312 ++V4H Variants/Loki Prime.mw4 2161 ++V4H Variants/Loki A.mw4 3347 ++V4H Variants/Loki B.mw4 2603 ++V4H Variants/Loki C.mw4 2068 ++V4H Variants/Loki F.mw4 1763 ++V4H Variants/Loki Frost.mw4 1833 ++V4H Variants/Loki G.mw4 1471 ++V4H Variants/Loki H.mw4 1645 ++V4H Variants/Loki Hellbringer 4.10.mw4 1822 ++V4H Variants/Loki Loki 4.10.mw4 1971 ++V4H Variants/Loki M.mw4 1521 ++V4H Variants/Loki Mischief 4.10.mw4 1865 ++V4H Variants/Loki MK II Prime.mw4 2094 ++V4H Variants/Loki MK II A.mw4 2680 ++V4H Variants/Loki MK II B.mw4 2126 ++V4H Variants/Longbow LGB-0W.mw4 2261 ++V4H Variants/Longbow LGB-12 C.mw4 2680 ++V4H Variants/Longbow LGB-13C.mw4 1652 ++V4H Variants/Longbow LGB-13NAIS.mw4 1650 ++V4H Variants/Longbow LGB-14C.mw4 1943 ++V4H Variants/Longbow LGB-7Q.mw4 1915 ++V4H Variants/Longbow LGB-7V.mw4 2257 ++V4H Variants/Longbow LGB-8V.mw4 1884 ++V4H Variants/Mad Cat Prime.mw4 2437 ++V4H Variants/Mad Cat (Bounty Hunter).mw4 1871 ++V4H Variants/Mad Cat (Pryde).mw4 1959 ++V4H Variants/Mad Cat A.mw4 2644 ++V4H Variants/Mad Cat B.mw4 1794 ++V4H Variants/Mad Cat C.mw4 1783 ++V4H Variants/Mad Cat D.mw4 2487 ++V4H Variants/Mad Cat E.mw4 1593 ++V4H Variants/Mad Cat H.mw4 1668 ++V4H Variants/Mad Cat M.mw4 1613 ++V4H Variants/Mad Cat Madcat 4.10.mw4 1820 ++V4H Variants/Mad Cat MKII 1.mw4 2441 ++V4H Variants/Mad Cat MKII 2.mw4 2621 ++V4H Variants/Mad Cat MKII 4.mw4 1961 ++V4H Variants/Mad Cat MKII Enhanced.mw4 1792 ++V4H Variants/Mad Cat MKII Wildonion.mw4 1897 ++V4H Variants/Mad Cat MKIV Prime.mw4 1979 ++V4H Variants/Mad Cat MKIV A.mw4 2468 ++V4H Variants/Mad Cat MKIV C.mw4 2082 ++V4H Variants/Mad Cat N.mw4 1811 ++V4H Variants/Mad Cat S.mw4 1938 ++V4H Variants/Mad Cat Sumo.mw4 2085 ++V4H Variants/Mad Cat T.mw4 1781 ++V4H Variants/Mad Cat TC.mw4 1795 ++V4H Variants/Mad Cat Timberwolf 4.10.mw4 2333 ++V4H Variants/Mad Cat W.mw4 1589 ++V4H Variants/Mad Cat Z.mw4 1918 ++V4H Variants/Mad Cat Zanin Neko 4.10.mw4 1787 ++V4H Variants/Marauder C.mw4 2047 ++V4H Variants/Marauder IIC.mw4 2199 ++V4H Variants/Marauder MAD-11D.mw4 1919 ++V4H Variants/Marauder MAD-1R.mw4 1522 ++V4H Variants/Marauder MAD-2R.mw4 1700 ++V4H Variants/Marauder MAD-2T.mw4 2164 ++V4H Variants/Marauder MAD-3D.mw4 1639 ++V4H Variants/Marauder MAD-3L.mw4 2023 ++V4H Variants/Marauder MAD-3M.mw4 1816 ++V4H Variants/Marauder MAD-3R.mw4 1970 ++V4H Variants/Marauder MAD-5CS.mw4 2316 ++V4H Variants/Marauder MAD-5S.mw4 2206 ++V4H Variants/Marauder MAD-7S.mw4 1559 ++V4H Variants/Marauder MAD-9D.mw4 1752 ++V4H Variants/Marauder MAD-9S.mw4 2509 ++V4H Variants/Masakari Prime.mw4 1869 ++V4H Variants/Masakari (Tara).mw4 2446 ++V4H Variants/Masakari A.mw4 1895 ++V4H Variants/Masakari B.mw4 2374 ++V4H Variants/Masakari C.mw4 2188 ++V4H Variants/Masakari D.mw4 1781 ++V4H Variants/Masakari F.mw4 1847 ++V4H Variants/Masakari H.mw4 2485 ++V4H Variants/Masakari Pyrotech.mw4 1953 ++V4H Variants/Mauler (Todesbote).mw4 1672 ++V4H Variants/Mauler Daboku DCMS-MX90.mw4 2236 ++V4H Variants/Mauler MAL-1K.mw4 2108 ++V4H Variants/Mauler MAL-1R.mw4 2401 ++V4H Variants/Mauler MAL-2R.mw4 1988 ++V4H Variants/Mauler MAL-3R.mw4 2292 ++V4H Variants/Mauler Reddog.mw4 2129 ++V4H Variants/Nova Cat Prime.mw4 2195 ++V4H Variants/Nova Cat A.mw4 2046 ++V4H Variants/Nova Cat B.mw4 2133 ++V4H Variants/Nova Cat C.mw4 1853 ++V4H Variants/Nova Cat D.mw4 2019 ++V4H Variants/Nova Cat E.mw4 1916 ++V4H Variants/Nova Cat F.mw4 1670 ++V4H Variants/Nova Cat H.mw4 1685 ++V4H Variants/Osiris OSR-3D.mw4 2812 ++V4H Variants/Osiris OSR-4D.mw4 1856 ++V4H Variants/Osiris OSR-5D.mw4 1122 ++V4H Variants/Owens Kotori 4.10.mw4 2316 ++V4H Variants/Owens OW-1 Prime.mw4 2328 ++V4H Variants/Owens OW-1A.mw4 2101 ++V4H Variants/Owens OW-1B.mw4 1388 ++V4H Variants/Owens OW-1C.mw4 1670 ++V4H Variants/Owens OW-1D.mw4 1205 ++V4H Variants/Owens OW-1E.mw4 1559 ++V4H Variants/Owens OW-1F.mw4 1420 ++V4H Variants/Owens OW-1R.mw4 1669 ++V4H Variants/Owens Owens 4.10.mw4 2540 ++V4H Variants/Puma Prime.mw4 1394 ++V4H Variants/Puma A.mw4 1754 ++V4H Variants/Puma B.mw4 2312 ++V4H Variants/Puma C.mw4 1886 ++V4H Variants/Puma D.mw4 1984 ++V4H Variants/Puma E.mw4 1706 ++V4H Variants/Puma H.mw4 1604 ++V4H Variants/Puma I.mw4 1690 ++V4H Variants/Puma S.mw4 2389 ++V4H Variants/Puma TC.mw4 1901 ++V4H Variants/Puma Wildonion.mw4 1605 ++V4H Variants/Raven RVN-1X.mw4 1416 ++V4H Variants/Raven RVN-2X.mw4 1652 ++V4H Variants/Raven RVN-3L.mw4 1497 ++V4H Variants/Raven RVN-3M.mw4 1662 ++V4H Variants/Raven RVN-3X.mw4 1980 ++V4H Variants/Raven RVN-4L.mw4 2586 ++V4H Variants/Raven RVN-4Lr.mw4 1493 ++V4H Variants/Raven RVN-SR (Shattere.mw4 1489 ++V4H Variants/Raven RVN-SS (Shattere.mw4 2708 ++V4H Variants/Raven Sumo.mw4 1927 ++V4H Variants/Raven X RVN-3X.mw4 1567 ++V4H Variants/Rifleman (Legend Kill 2).mw4 2097 ++V4H Variants/Rifleman (Legend Kill).mw4 1939 ++V4H Variants/Rifleman C 2.mw4 1414 ++V4H Variants/Rifleman C.mw4 1774 ++V4H Variants/Rifleman C3.mw4 2178 ++V4H Variants/Rifleman II (Kataga).mw4 2008 ++V4H Variants/Rifleman II RFL-3N-2 (LK).mw4 1943 ++V4H Variants/Rifleman II RFL-3N-2.mw4 2038 ++V4H Variants/Rifleman IIC 2.mw4 2194 ++V4H Variants/Rifleman IIC 3.mw4 1709 ++V4H Variants/Rifleman IIC 4.mw4 1594 ++V4H Variants/Rifleman IIC 5.mw4 1872 ++V4H Variants/Rifleman IIC.mw4 2145 ++V4H Variants/Rifleman RFL-3C.mw4 1364 ++V4H Variants/Rifleman RFL-3Cr.mw4 1382 ++V4H Variants/Rifleman RFL-3N.mw4 1526 ++V4H Variants/Rifleman RFL-4D.mw4 1551 ++V4H Variants/Rifleman RFL-5CS.mw4 1919 ++V4H Variants/Rifleman RFL-5D.mw4 2120 ++V4H Variants/Rifleman RFL-5M.mw4 1790 ++V4H Variants/Rifleman RFL-6D.mw4 1713 ++V4H Variants/Rifleman RFL-6X.mw4 1863 ++V4H Variants/Rifleman RFL-7G.mw4 1816 ++V4H Variants/Rifleman RFL-7M.mw4 1721 ++V4H Variants/Rifleman RFL-7N.mw4 1517 ++V4H Variants/Rifleman RFL-7N2.mw4 1544 ++V4H Variants/Rifleman RFL-7X.mw4 1448 ++V4H Variants/Rifleman RFL-8X.mw4 1739 ++V4H Variants/Rifleman RFL-9T.mw4 1928 ++V4H Variants/Rifleman Wildonion.mw4 1839 ++V4H Variants/Ryoken Prime.mw4 2516 ++V4H Variants/Ryoken (Attwater).mw4 1638 ++V4H Variants/Ryoken (Kotare).mw4 1697 ++V4H Variants/Ryoken A.mw4 2417 ++V4H Variants/Ryoken Anubis.mw4 2248 ++V4H Variants/Ryoken B.mw4 1834 ++V4H Variants/Ryoken C.mw4 1415 ++V4H Variants/Ryoken D.mw4 2290 ++V4H Variants/Ryoken E.mw4 1974 ++V4H Variants/Ryoken Frost.mw4 1808 ++V4H Variants/Ryoken I.mw4 2133 ++V4H Variants/Ryoken II (Tassa).mw4 1868 ++V4H Variants/Ryoken II .mw4 2776 ++V4H Variants/Ryoken II 2.mw4 1817 ++V4H Variants/Ryoken P.mw4 1684 ++V4H Variants/Ryoken T.mw4 2133 ++V4H Variants/Ryoken TC.mw4 1589 ++V4H Variants/Ryoken Z.mw4 2435 ++V4H Variants/Shadow Cat Prime.mw4 1514 ++V4H Variants/Shadow Cat A.mw4 1677 ++V4H Variants/Shadow Cat B.mw4 2189 ++V4H Variants/Shadow Cat C.mw4 1790 ++V4H Variants/Shadow Cat D.mw4 1557 ++V4H Variants/Shadow Cat H.mw4 2001 ++V4H Variants/Shadow Cat I.mw4 1922 ++V4H Variants/Shadow Cat II 2.mw4 1500 ++V4H Variants/Shadow Cat II 3.mw4 1909 ++V4H Variants/Shadow Cat II 4.mw4 1334 ++V4H Variants/Shadow Cat III Prime.mw4 1907 ++V4H Variants/Shadow Cat III A.mw4 1320 ++V4H Variants/Shadow Cat III B.mw4 1824 ++V4H Variants/Shadow Cat III C.mw4 1626 ++V4H Variants/Shadow Cat M.mw4 1549 ++V4H Variants/Shadow Cat Pharaoh.mw4 2129 ++V4H Variants/Shadow Cat T.mw4 1520 ++V4H Variants/Shadow Cat TC.mw4 1693 ++V4H Variants/Solitaire 1.mw4 2264 ++V4H Variants/Solitaire 2.mw4 1227 ++V4H Variants/Sunder (Samual).mw4 2586 ++V4H Variants/Sunder Chinra Camp.mw4 1809 ++V4H Variants/Sunder Denkou 4.10.mw4 2319 ++V4H Variants/Sunder SD1-O Prime.mw4 2018 ++V4H Variants/Sunder SD1-O A.mw4 1803 ++V4H Variants/Sunder SD1-O C.mw4 2135 ++V4H Variants/Sunder SD1-O D.mw4 1566 ++V4H Variants/Sunder SD1-O E.mw4 1594 ++V4H Variants/Sunder SD1-O R.mw4 1775 ++V4H Variants/Sunder SD1-OF.mw4 1870 ++V4H Variants/Sunder Sunder 4.10.mw4 2084 ++V4H Variants/Templar (Grayson).mw4 2710 ++V4H Variants/Templar III TLR2-O Prim.mw4 1741 ++V4H Variants/Templar III TLR2-OA.mw4 1541 ++V4H Variants/Templar III TLR2-OD.mw4 1497 ++V4H Variants/Templar TLR1-O Prime.mw4 2417 ++V4H Variants/Templar TLR1-O A.mw4 2021 ++V4H Variants/Templar TLR1-O C.mw4 2183 ++V4H Variants/Templar TLR1-O D.mw4 1379 ++V4H Variants/Templar TLR1-O E.mw4 2671 ++V4H Variants/Templar TLR1-O F.mw4 1963 ++V4H Variants/Templar TLR1-O G.mw4 1738 ++V4H Variants/Templar TLR1-O H.mw4 1618 ++V4H Variants/Templar TLR1-O I.mw4 1461 ++V4H Variants/Thanatos Black Drake.mw4 1748 ++V4H Variants/Thanatos Plainsrider.mw4 2484 ++V4H Variants/Thanatos TNS-4S.mw4 2025 ++V4H Variants/Thanatos TNS-4T.mw4 1753 ++V4H Variants/Thanatos TNS-6S.mw4 1753 ++V4H Variants/Thor Prime.mw4 1726 ++V4H Variants/Thor A.mw4 1612 ++V4H Variants/Thor AA.mw4 1420 ++V4H Variants/Thor B.mw4 2169 ++V4H Variants/Thor C.mw4 1950 ++V4H Variants/Thor D.mw4 1842 ++V4H Variants/Thor E.mw4 1506 ++V4H Variants/Thor F.mw4 1906 ++V4H Variants/Thor G.mw4 2387 ++V4H Variants/Thor II Prime.mw4 2029 ++V4H Variants/Thor II A.mw4 1729 ++V4H Variants/Thor II B.mw4 1899 ++V4H Variants/Thor II C.mw4 1521 ++V4H Variants/Thor II D.mw4 1735 ++V4H Variants/Thor M.mw4 2153 ++V4H Variants/Thor Pharaoh.mw4 1308 ++V4H Variants/Thor Pyrotech.mw4 2214 ++V4H Variants/Thor Q.mw4 1864 ++V4H Variants/Thor Summoner 4.10.mw4 2079 ++V4H Variants/Thor Thor 4.10.mw4 1897 ++V4H Variants/Thunderbolt (Ilyena).mw4 1877 ++V4H Variants/Thunderbolt C.mw4 1994 ++V4H Variants/Thunderbolt IIC.mw4 2379 ++V4H Variants/Thunderbolt TDR-10M.mw4 1713 ++V4H Variants/Thunderbolt TDR-10S.mw4 1625 ++V4H Variants/Thunderbolt TDR-10SE.mw4 2324 ++V4H Variants/Thunderbolt TDR-11SE.mw4 1729 ++V4H Variants/Thunderbolt TDR-17S.mw4 1953 ++V4H Variants/Thunderbolt TDR-1C.mw4 1758 ++V4H Variants/Thunderbolt TDR-5D.mw4 1296 ++V4H Variants/Thunderbolt TDR-5S-T (Tallma.mw4 1682 ++V4H Variants/Thunderbolt TDR-5S.mw4 1914 ++V4H Variants/Thunderbolt TDR-5Sb.mw4 2404 ++V4H Variants/Thunderbolt TDR-5SE.mw4 1582 ++V4H Variants/Thunderbolt TDR-5SS.mw4 1908 ++V4H Variants/Thunderbolt TDR-60-RLA.mw4 2365 ++V4H Variants/Thunderbolt TDR-7M.mw4 1750 ++V4H Variants/Thunderbolt TDR-7SE.mw4 1574 ++V4H Variants/Thunderbolt TDR-9M.mw4 1426 ++V4H Variants/Thunderbolt TDR-9S.mw4 2500 ++V4H Variants/Thunderbolt TDR-9SE.mw4 1929 ++V4H Variants/Thunderbolt TDR-9T.mw4 1965 ++V4H Variants/Uller Prime.mw4 1432 ++V4H Variants/Uller A.mw4 1450 ++V4H Variants/Uller B.mw4 1702 ++V4H Variants/Uller C.mw4 2190 ++V4H Variants/Uller D.mw4 1696 ++V4H Variants/Uller E.mw4 1451 ++V4H Variants/Uller G.mw4 1512 ++V4H Variants/Uller I.mw4 1430 ++V4H Variants/Uller Little Stinker.mw4 1619 ++V4H Variants/Uller S.mw4 2388 ++V4H Variants/Uller W.mw4 1660 ++V4H Variants/Urbanmech IIC.mw4 1298 ++V4H Variants/Urbanmech UM-AIV.mw4 1126 ++V4H Variants/Urbanmech UM-R60.mw4 1118 ++V4H Variants/Urbanmech UM-R60L.mw4 1152 ++V4H Variants/Urbanmech UM-R63.mw4 1263 ++V4H Variants/Urbanmech UM-R68.mw4 1357 ++V4H Variants/Urbanmech UM-R69.mw4 1500 ++V4H Variants/Urbanmech UM-R80.mw4 1818 ++V4H Variants/Urbanmech UM-R93.mw4 1336 ++V4H Variants/Uziel (Jacob 2).mw4 1588 ++V4H Variants/Uziel Sumo.mw4 2210 ++V4H Variants/Uziel UZL-2S.mw4 1964 ++V4H Variants/Uziel UZL-3S.mw4 1760 ++V4H Variants/Uziel UZL-8S.mw4 1892 ++V4H Variants/Victor (Li).mw4 2262 ++V4H Variants/Victor C.mw4 1583 ++V4H Variants/Victor VTR-10D.mw4 1479 ++V4H Variants/Victor VTR-10L.mw4 2326 ++V4H Variants/Victor VTR-10S.mw4 2003 ++V4H Variants/Victor VTR-9A.mw4 2150 ++V4H Variants/Victor VTR-9A1.mw4 1527 ++V4H Variants/Victor VTR-9B.mw4 1508 ++V4H Variants/Victor VTR-9K.mw4 1995 ++V4H Variants/Victor VTR-9K2 (St. Jam.mw4 1571 ++V4H Variants/Victor VTR-9S.mw4 1499 ++V4H Variants/Victor VTR-C.mw4 1984 ++V4H Variants/Vulture Prime.mw4 2127 ++V4H Variants/Vulture A.mw4 2682 ++V4H Variants/Vulture B.mw4 2331 ++V4H Variants/Vulture C.mw4 1383 ++V4H Variants/Vulture Chinra.mw4 1941 ++V4H Variants/Vulture D.mw4 1665 ++V4H Variants/Vulture DD.mw4 1542 ++V4H Variants/Vulture H.mw4 1812 ++V4H Variants/Vulture III Prime.mw4 1602 ++V4H Variants/Vulture III A.mw4 1572 ++V4H Variants/Vulture III C.mw4 1990 ++V4H Variants/Vulture Mad Dog 4.10.mw4 1775 ++V4H Variants/Vulture MK IV Prime.mw4 1559 ++V4H Variants/Vulture MK IV A.mw4 1682 ++V4H Variants/Vulture MK IV C.mw4 1506 ++V4H Variants/Vulture MK IV D.mw4 1704 ++V4H Variants/Vulture T.mw4 1624 ++V4H Variants/Vulture V.mw4 1579 ++V4H Variants/Vulture Vulture 4.10.mw4 1779 ++V4H Variants/Warhammer C.mw4 2537 ++V4H Variants/Warhammer C2.mw4 1760 ++V4H Variants/Warhammer C3.mw4 1887 ++V4H Variants/Warhammer IIC 10.mw4 2054 ++V4H Variants/Warhammer IIC 11.mw4 1711 ++V4H Variants/Warhammer IIC 12.mw4 2055 ++V4H Variants/Warhammer IIC 13.mw4 1962 ++V4H Variants/Warhammer IIC 2.mw4 2093 ++V4H Variants/Warhammer IIC 3.mw4 1938 ++V4H Variants/Warhammer IIC 4.mw4 2700 ++V4H Variants/Warhammer IIC 7.mw4 3117 ++V4H Variants/Warhammer IIC.mw4 1980 ++V4H Variants/Warhammer WHD-10CT.mw4 1794 ++V4H Variants/Warhammer WHM-10K.mw4 1771 ++V4H Variants/Warhammer WHM-10T.mw4 1340 ++V4H Variants/Warhammer WHM-11T.mw4 1688 ++V4H Variants/Warhammer WHM-4L.mw4 2104 ++V4H Variants/Warhammer WHM-5L.mw4 1565 ++V4H Variants/Warhammer WHM-6D.mw4 2303 ++V4H Variants/Warhammer WHM-6K.mw4 2087 ++V4H Variants/Warhammer WHM-6L.mw4 2476 ++V4H Variants/Warhammer WHM-6R.mw4 2566 ++V4H Variants/Warhammer WHM-6Rb.mw4 1829 ++V4H Variants/Warhammer WHM-7A.mw4 1751 ++V4H Variants/Warhammer WHM-7CS.mw4 1916 ++V4H Variants/Warhammer WHM-7K.mw4 3077 ++V4H Variants/Warhammer WHM-7M-DC.mw4 1623 ++V4H Variants/Warhammer WHM-7M.mw4 2967 ++V4H Variants/Warhammer WHM-7S.mw4 2110 ++V4H Variants/Warhammer WHM-8D.mw4 2565 ++V4H Variants/Warhammer WHM-8K.mw4 1599 ++V4H Variants/Warhammer WHM-8M.mw4 2030 ++V4H Variants/Warhammer WHM-8R.mw4 1768 ++V4H Variants/Warhammer WHM-9D.mw4 2083 ++V4H Variants/Warhammer WHM-9K.mw4 1503 ++V4H Variants/Warhammer WHM-9S.mw4 2063 ++V4H Variants/Wolfhound (Allard).mw4 1835 ++V4H Variants/Wolfhound IIC (Grinner).mw4 2089 ++V4H Variants/Wolfhound WLF-1.mw4 1338 ++V4H Variants/Wolfhound WLF-1A.mw4 2174 ++V4H Variants/Wolfhound WLF-1B.mw4 2399 ++V4H Variants/Wolfhound WLF-2.mw4 1906 ++V4H Variants/Wolfhound WLF-2H.mw4 1711 ++V4H Variants/Wolfhound WLF-2X.mw4 1691 ++V4H Variants/Wolfhound WLF-3 S.mw4 1826 ++V4H Variants/Wolfhound WLF-3M.mw4 1500 ++V4H Variants/Wolfhound WLF-4W.mw4 1622 ++V4H Variants/Wolfhound WLF-4WA.mw4 1509 ++V4H Variants/Wolfhound WLF-5.mw4 1689 ++V4H Variants/Zeus (Leonidas).mw4 1784 ++V4H Variants/Zeus (Stacy).mw4 1575 ++V4H Variants/Zeus ZEU-10WB.mw4 1778 ++V4H Variants/Zeus ZEU-11S.mw4 1607 ++V4H Variants/Zeus ZEU-5S.mw4 1984 ++V4H Variants/Zeus ZEU-5T.mw4 1839 ++V4H Variants/Zeus ZEU-6A.mw4 1635 ++V4H Variants/Zeus ZEU-6S.mw4 1538 ++V4H Variants/Zeus ZEU-6T.mw4 1775 ++V4H Variants/Zeus ZEU-6Y.mw4 1436 ++V4H Variants/Zeus ZEU-9S-DC.mw4 1612 ++V4H Variants/Zeus ZEU-9S.mw4 2007 ++V4H Variants/Zeus ZEU-9S2.mw4 2200 ++V4H Variants/Zeus ZEU-9T.mw4 1714 ++V4H Variants/Zeus ZEU-9WD.mw4 1504 ++V4H Variants/Zeus ZEU-X.mw4 1414 ++V4H Variants/Zeus ZEU-X2.mw4 1538 ++V4H Variants/Zeus ZEU-X3.mw4 1721 ++V4H Variants/Zeus ZEU-X4.mw4 1369 +-OURS core.dep 2055332 +-OURS maps/colsm01_backup.dep 75 +-OURS maps/colsm01_backup.mw4 76 +-OURS maps/colsm02.dep 29960 +-OURS maps/conroe01.dep 8617 +-OURS maps/conroe01.mw4 3942680 +-OURS maps/conroe02.dep 9832 +-OURS maps/conroe02.mw4 5121163 +-OURS maps/conroe03.dep 8581 +-OURS maps/conroe03.mw4 4450895 +-OURS maps/ddc_msl.dep 17484 +-OURS maps/ddc_msl.mw4 11559703 +-OURS maps/desert.dep 27519 +-OURS maps/desert.mw4 21234901 +-OURS maps/doneg01.dep 51130 +-OURS maps/doneg01.mw4 23527721 +-OURS maps/freezer.dep 28130 +-OURS maps/freezer.mw4 7064929 +-OURS maps/gage.dep 47112 +-OURS maps/gage.mw4 24653343 +-OURS maps/minerl01.dep 45062 +-OURS maps/minerl01.mw4 25342479 +-OURS maps/mountn01.dep 53045 +-OURS maps/mountn01.mw4 26234762 +-OURS maps/mountn03.dep 53426 +-OURS maps/mountn03.mw4 27117600 +-OURS maps/ngoth.dep 37159 +-OURS maps/ngoth.mw4 14525725 +-OURS maps/ruin03.dep 48398 +-OURS maps/ruin03.mw4 14366816 +-OURS maps/scrub01.dep 55325 +-OURS maps/scrub01.mw4 25611032 +-OURS maps/scrub06.dep 30043 +-OURS maps/scrub06.mw4 15607259 +-OURS maps/stormcanyonsiege_backup.dep 84 +-OURS maps/stormcanyonsiege_backup.mw4 85 +-OURS maps/volcan01.dep 31180 +-OURS maps/volcan01.mw4 15406783 +-OURS maps/volcan03.dep 23226 +-OURS maps/volcan03.mw4 8145162 +-OURS Missions/aspen.dep 13976 +-OURS Missions/aspen.mw4 1604417 +-OURS Missions/canyon.dep 3602 +-OURS Missions/canyon.mw4 104802 +-OURS Missions/coliseum_backup.dep 106 +-OURS Missions/coliseum_backup.mw4 106 +-OURS Missions/conroe01.dep 12199 +-OURS Missions/conroe01.mw4 1189717 +-OURS Missions/conroe02.dep 12557 +-OURS Missions/conroe02.mw4 1422846 +-OURS Missions/conroe03.dep 12642 +-OURS Missions/conroe03.mw4 1479474 +-OURS Missions/editortemplate.dep 13624 +-OURS Missions/editortemplate.mw4 1582532 +-OURS Missions/gagetown.dep 4093 +-OURS Missions/gagetown.mw4 283015 +-OURS Missions/gladiatorpit.dep 3850 +-OURS Missions/gladiatorpit.mw4 55728 +-OURS Missions/lakeside.dep 4198 +-OURS Missions/lakeside.mw4 203961 +-OURS Missions/mechworks.dep 3266 +-OURS Missions/mechworks.mw4 85175 +-OURS Missions/minehq.dep 2955 +-OURS Missions/minehq.mw4 175811 +-OURS Missions/newgothem.dep 3462 +-OURS Missions/newgothem.mw4 143905 +-OURS Missions/rubble.dep 12144 +-OURS Missions/rubble.mw4 224326 +-OURS Missions/sanddunes.dep 22443 +-OURS Missions/sanddunes.mw4 308302 +-OURS Missions/spaceport.dep 12752 +-OURS Missions/spaceport.mw4 147739 +-OURS Missions/vbase.dep 3429 +-OURS Missions/vbase.mw4 145518 +-OURS props.dep 1479888 +-OURS skies.dep 2738603 +-OURS skies.mw4 39970351 +-OURS textures.dep 931747 +-OURS UserMissions/desert.dep 13413 +-OURS UserMissions/desert.mw4 1187653 +-OURS UserMissions/phoenixpalacestb.dep 8411 +-OURS UserMissions/phoenixpalacestb.mw4 669163 +-OURS UserMissions/s1s1.dep 7415 +-OURS UserMissions/s1s1.mw4 776380 +-OURS UserMissions/s1s1.nfo 219 +-OURS UserMissions/s1s1.tga 37650 +-OURS UserMissions/s1s2.dep 7694 +-OURS UserMissions/s1s2.mw4 760651 +-OURS UserMissions/s1s2.nfo 219 +-OURS UserMissions/s1s2.tga 37650 +-OURS UserMissions/s1s3.dep 7178 +-OURS UserMissions/s1s3.mw4 695951 +-OURS UserMissions/s1s3.nfo 219 +-OURS UserMissions/s1s3.tga 37650 +~DIF core.mw4 V4H=2435919B OURS=2306050B +~DIF maps/alpine02.dep V4H=57080B OURS=57080B +~DIF maps/alpine02.mw4 V4H=25169428B OURS=25169428B +~DIF maps/arctic04.dep V4H=42642B OURS=42642B +~DIF maps/arctic04.mw4 V4H=25158362B OURS=25158362B +~DIF maps/arctic06.dep V4H=42778B OURS=42778B +~DIF maps/arctic06.mw4 V4H=25601217B OURS=25601217B +~DIF maps/colsm01.dep V4H=21122B OURS=21122B +~DIF maps/colsm01.mw4 V4H=8914796B OURS=8914797B +~DIF maps/colsm02.mw4 V4H=7927455B OURS=7927465B +~DIF maps/darklord.dep V4H=18012B OURS=18012B +~DIF maps/darklord.mw4 V4H=10875446B OURS=10875446B +~DIF maps/desert07.dep V4H=48547B OURS=48547B +~DIF maps/desert07.mw4 V4H=25025521B OURS=25025523B +~DIF maps/fact01.dep V4H=24482B OURS=24482B +~DIF maps/fact01.mw4 V4H=5424149B OURS=5424149B +~DIF maps/firestorm.dep V4H=8560B OURS=8560B +~DIF maps/firestorm.mw4 V4H=5455361B OURS=5455362B +~DIF maps/grassland.dep V4H=39230B OURS=39230B +~DIF maps/grassland.mw4 V4H=18132745B OURS=18132745B +~DIF maps/hotplate.dep V4H=29122B OURS=29122B +~DIF maps/hotplate.mw4 V4H=19877433B OURS=19877434B +~DIF maps/ice3.dep V4H=25960B OURS=25960B +~DIF maps/ice3.mw4 V4H=7616820B OURS=7616821B +~DIF maps/jung02.dep V4H=22077B OURS=22077B +~DIF maps/jung02.mw4 V4H=6402081B OURS=6402082B +~DIF maps/lunar01.dep V4H=29743B OURS=29743B +~DIF maps/lunar01.mw4 V4H=14510631B OURS=14510632B +~DIF maps/minerl03.dep V4H=44838B OURS=44838B +~DIF maps/minerl03.mw4 V4H=26376623B OURS=26376624B +~DIF maps/nazca.dep V4H=27557B OURS=27557B +~DIF maps/nazca.mw4 V4H=13541329B OURS=13541330B +~DIF maps/palace01.dep V4H=29647B OURS=29647B +~DIF maps/palace01.mw4 V4H=14719260B OURS=14719260B +~DIF maps/peaks.dep V4H=26786B OURS=26786B +~DIF maps/peaks.mw4 V4H=22096166B OURS=22096167B +~DIF maps/reduex.dep V4H=28816B OURS=28816B +~DIF maps/reduex.mw4 V4H=13009203B OURS=13009204B +~DIF maps/rookiearena2-terrain-v1.dep V4H=11658B OURS=11658B +~DIF maps/rookiearena2-terrain-v1.mw4 V4H=5114100B OURS=5114102B +~DIF maps/scrub02.dep V4H=43866B OURS=43866B +~DIF maps/scrub02.mw4 V4H=23852026B OURS=23852026B +~DIF maps/stormcanyon.dep V4H=18304B OURS=18304B +~DIF maps/stormcanyon.mw4 V4H=7946254B OURS=7946255B +~DIF maps/stormcanyonsiege.dep V4H=22075B OURS=22075B +~DIF maps/stormcanyonsiege.mw4 V4H=11041868B OURS=11041869B +~DIF maps/swamp01.dep V4H=60052B OURS=60052B +~DIF maps/swamp01.mw4 V4H=24364771B OURS=24364771B +~DIF maps/urban01.dep V4H=56415B OURS=56415B +~DIF maps/urban01.mw4 V4H=14872939B OURS=14872939B +~DIF maps/urban02.dep V4H=51432B OURS=51432B +~DIF maps/urban02.mw4 V4H=14109022B OURS=14109022B +~DIF maps/urban05.dep V4H=51432B OURS=51432B +~DIF maps/urban05.mw4 V4H=13443822B OURS=13443822B +~DIF Missions/bigcity.dep V4H=9012B OURS=9012B +~DIF Missions/bigcity.mw4 V4H=861215B OURS=888139B +~DIF Missions/cantina.dep V4H=4173B OURS=4173B +~DIF Missions/cantina.mw4 V4H=189512B OURS=189209B +~DIF Missions/cantinasiege.dep V4H=8813B OURS=8813B +~DIF Missions/cantinasiege.mw4 V4H=208456B OURS=208556B +~DIF Missions/coliseum.dep V4H=7448B OURS=7448B +~DIF Missions/coliseum.mw4 V4H=81384B OURS=84766B +~DIF Missions/cpark.dep V4H=4562B OURS=4562B +~DIF Missions/cpark.mw4 V4H=203761B OURS=204900B +~DIF Missions/dustbowl.dep V4H=3740B OURS=3740B +~DIF Missions/dustbowl.mw4 V4H=193058B OURS=193400B +~DIF Missions/factory.dep V4H=3267B OURS=3267B +~DIF Missions/factory.mw4 V4H=61759B OURS=78497B +~DIF Missions/fbite.dep V4H=4408B OURS=4408B +~DIF Missions/fbite.mw4 V4H=238641B OURS=238937B +~DIF Missions/freezer.dep V4H=15148B OURS=15148B +~DIF Missions/freezer.mw4 V4H=2571361B OURS=1250593B +~DIF Missions/gbait.dep V4H=4497B OURS=4497B +~DIF Missions/gbait.mw4 V4H=237750B OURS=242084B +~DIF Missions/ghosthighway.dep V4H=4346B OURS=4346B +~DIF Missions/ghosthighway.mw4 V4H=142521B OURS=143541B +~DIF Missions/grassland.dep V4H=15307B OURS=15307B +~DIF Missions/grassland.mw4 V4H=1341128B OURS=1343923B +~DIF Missions/hideaway.dep V4H=14227B OURS=14227B +~DIF Missions/hideaway.mw4 V4H=1809779B OURS=1811571B +~DIF Missions/hotplate.dep V4H=14215B OURS=14215B +~DIF Missions/hotplate.mw4 V4H=1734581B OURS=1737958B +~DIF Missions/icity.dep V4H=3696B OURS=3696B +~DIF Missions/icity.mw4 V4H=187449B OURS=191854B +~DIF Missions/jungle.dep V4H=6156B OURS=6156B +~DIF Missions/jungle.mw4 V4H=89414B OURS=91144B +~DIF Missions/lunacy.dep V4H=3166B OURS=3166B +~DIF Missions/lunacy.mw4 V4H=130594B OURS=132141B +~DIF Missions/nazca.dep V4H=13831B OURS=13831B +~DIF Missions/nazca.mw4 V4H=1152744B OURS=1154468B +~DIF Missions/peaks.dep V4H=13240B OURS=13240B +~DIF Missions/peaks.mw4 V4H=2008910B OURS=2010317B +~DIF Missions/pgates.dep V4H=20136B OURS=20136B +~DIF Missions/pgates.mw4 V4H=1892087B OURS=1891814B +~DIF Missions/reduex.dep V4H=18021B OURS=18021B +~DIF Missions/reduex.mw4 V4H=982426B OURS=987878B +~DIF Missions/reduexsiege.dep V4H=21383B OURS=21383B +~DIF Missions/reduexsiege.mw4 V4H=1045559B OURS=1055466B +~DIF Missions/scarabstronghold.dep V4H=15325B OURS=15325B +~DIF Missions/scarabstronghold.mw4 V4H=1231984B OURS=1235497B +~DIF Missions/snowjob.dep V4H=3714B OURS=3714B +~DIF Missions/snowjob.mw4 V4H=198479B OURS=198887B +~DIF Missions/stormcanyon.dep V4H=25427B OURS=25427B +~DIF Missions/stormcanyon.mw4 V4H=1389035B OURS=1391328B +~DIF Missions/stormcanyonsiege.dep V4H=22358B OURS=22358B +~DIF Missions/stormcanyonsiege.mw4 V4H=1460317B OURS=1464368B +~DIF Missions/tline.dep V4H=4530B OURS=4530B +~DIF Missions/tline.mw4 V4H=303148B OURS=304936B +~DIF Missions/tribeincursion.dep V4H=8926B OURS=8926B +~DIF Missions/tribeincursion.mw4 V4H=724749B OURS=726414B +~DIF Missions/tribeincursionmission.dep V4H=10785B OURS=10785B +~DIF Missions/tribeincursionmission.mw4 V4H=752493B OURS=754435B +~DIF Pilots/Tesla/options.mw4 V4H=1455B OURS=1666B +~DIF props.mw4 V4H=114253354B OURS=113463584B +~DIF textures.mw4 V4H=263529356B OURS=257314405B diff --git a/MW4COMPARE/reports/textures-decoded-diff.txt b/MW4COMPARE/reports/textures-decoded-diff.txt new file mode 100644 index 00000000..2ed163fe --- /dev/null +++ b/MW4COMPARE/reports/textures-decoded-diff.txt @@ -0,0 +1,301 @@ +# entries: A=10419 B=10120 A_only=299 B_only=0 decoded-differ=1 ++A mechs/champion/champion_cage.erf 11787B ++A mechs/champion/chp_hip.erf 6655B ++A mechs/champion/chp_hip_dam.erf 6655B ++A mechs/champion/chp_lbtoe.erf 1211B ++A mechs/champion/chp_lbtoe_dam.erf 1211B ++A mechs/champion/chp_ldleg.erf 8372B ++A mechs/champion/chp_ldleg_dam.erf 8372B ++A mechs/champion/chp_lfoot.erf 2017B ++A mechs/champion/chp_lfoot_dam.erf 2017B ++A mechs/champion/chp_lftoe.erf 2309B ++A mechs/champion/chp_lftoe_dam.erf 2309B ++A mechs/champion/chp_lgun.erf 8328B ++A mechs/champion/chp_luarm.erf 853B ++A mechs/champion/chp_luarm_dam.erf 853B ++A mechs/champion/chp_luleg.erf 10067B ++A mechs/champion/chp_luleg_dam.erf 10067B ++A mechs/champion/chp_rbtoe.erf 1211B ++A mechs/champion/chp_rbtoe_dam.erf 1211B ++A mechs/champion/chp_rdleg.erf 8372B ++A mechs/champion/chp_rdleg_dam.erf 8372B ++A mechs/champion/chp_rfoot.erf 2017B ++A mechs/champion/chp_rfoot_dam.erf 2017B ++A mechs/champion/chp_rftoe.erf 2309B ++A mechs/champion/chp_rftoe_dam.erf 2309B ++A mechs/champion/chp_rgun.erf 8328B ++A mechs/champion/chp_ruarm.erf 853B ++A mechs/champion/chp_ruarm_dam.erf 853B ++A mechs/champion/chp_ruleg.erf 10067B ++A mechs/champion/chp_ruleg_dam.erf 10067B ++A mechs/champion/chp_specialone.erf 3759B ++A mechs/champion/chp_specialone_dam.erf 1290B ++A mechs/champion/chp_specialtwo.erf 3759B ++A mechs/champion/chp_specialtwo_dam.erf 1290B ++A mechs/champion/chp_torso.erf 63022B ++A mechs/champion/chp_torso_dam.erf 63022B ++A mechs/champion/runninglights.erf 1552B ++A mechs/champion_destroyed/champion_destroyed.erf 14157B ++A mechs/dasher/das_hip.erf 51546B ++A mechs/dasher/das_hip_dam.erf 51546B ++A mechs/dasher/das_ldleg.erf 20552B ++A mechs/dasher/das_ldleg_dam.erf 20552B ++A mechs/dasher/das_lfoot.erf 11228B ++A mechs/dasher/das_lfoot_dam.erf 11228B ++A mechs/dasher/das_lgun.erf 27396B ++A mechs/dasher/das_lgun_dam.erf 783B ++A mechs/dasher/das_ltoe.erf 5172B ++A mechs/dasher/das_ltoe_dam.erf 5172B ++A mechs/dasher/das_luarm.erf 15258B ++A mechs/dasher/das_luarm_dam.erf 15258B ++A mechs/dasher/das_luleg.erf 10648B ++A mechs/dasher/das_luleg_dam.erf 10648B ++A mechs/dasher/das_rdleg.erf 20260B ++A mechs/dasher/das_rdleg_dam.erf 20260B ++A mechs/dasher/das_rfoot.erf 11228B ++A mechs/dasher/das_rfoot_dam.erf 11228B ++A mechs/dasher/das_rgun.erf 27396B ++A mechs/dasher/das_rtoe.erf 5172B ++A mechs/dasher/das_rtoe_dam.erf 5172B ++A mechs/dasher/das_ruarm.erf 15226B ++A mechs/dasher/das_ruarm_dam.erf 943B ++A mechs/dasher/das_ruleg.erf 10692B ++A mechs/dasher/das_ruleg_dam.erf 10692B ++A mechs/dasher/das_torso.erf 81366B ++A mechs/dasher/das_torso_dam.erf 81366B ++A mechs/dasher/dasher_cage.erf 12523B ++A mechs/dasher/runninglights__.erf 10824B ++A mechs/dasher_destroyed/dasher_destroyed.erf 14624B ++A mechs/griffin/grf_hip.erf 2831B ++A mechs/griffin/grf_hip_dam.erf 2831B ++A mechs/griffin/grf_lbtoe.erf 4029B ++A mechs/griffin/grf_lbtoe_dam.erf 4029B ++A mechs/griffin/grf_ldleg.erf 5784B ++A mechs/griffin/grf_ldleg_dam.erf 5784B ++A mechs/griffin/grf_lfoot.erf 791B ++A mechs/griffin/grf_lfoot_dam.erf 791B ++A mechs/griffin/grf_lftoe.erf 1479B ++A mechs/griffin/grf_lftoe_dam.erf 1479B ++A mechs/griffin/grf_lgun.erf 16111B ++A mechs/griffin/grf_lgun_dam.erf 16111B ++A mechs/griffin/grf_luarm.erf 4819B ++A mechs/griffin/grf_luarm_dam.erf 4819B ++A mechs/griffin/grf_luleg.erf 4933B ++A mechs/griffin/grf_luleg_dam.erf 4933B ++A mechs/griffin/grf_rbtoe.erf 4029B ++A mechs/griffin/grf_rbtoe_dam.erf 4029B ++A mechs/griffin/grf_rdleg.erf 5784B ++A mechs/griffin/grf_rdleg_dam.erf 5784B ++A mechs/griffin/grf_rfoot.erf 791B ++A mechs/griffin/grf_rfoot_dam.erf 791B ++A mechs/griffin/grf_rftoe.erf 1479B ++A mechs/griffin/grf_rftoe_dam.erf 1479B ++A mechs/griffin/grf_rgun.erf 14785B ++A mechs/griffin/grf_rgun_dam.erf 14785B ++A mechs/griffin/grf_ruarm.erf 4819B ++A mechs/griffin/grf_ruarm_dam.erf 4819B ++A mechs/griffin/grf_ruleg.erf 4933B ++A mechs/griffin/grf_ruleg_dam.erf 4933B ++A mechs/griffin/grf_specialone.erf 2511B ++A mechs/griffin/grf_specialone_dam.erf 2559B ++A mechs/griffin/grf_specialtwo.erf 3659B ++A mechs/griffin/grf_specialtwo_dam.erf 3707B ++A mechs/griffin/grf_torso.erf 24032B ++A mechs/griffin/grf_torso_dam.erf 24032B ++A mechs/griffin/griffin_cage.erf 13884B ++A mechs/griffin/runninglights.erf 541B ++A mechs/griffin_destroyed/griffin_destroyed.erf 16697B ++A mechs/jenner2c/jec_hip.erf 3936B ++A mechs/jenner2c/jec_hip_dam.erf 3936B ++A mechs/jenner2c/jec_lbtoe.erf 3191B ++A mechs/jenner2c/jec_lbtoe_dam.erf 3191B ++A mechs/jenner2c/jec_ldleg.erf 8438B ++A mechs/jenner2c/jec_ldleg_dam.erf 8438B ++A mechs/jenner2c/jec_lfoot.erf 2667B ++A mechs/jenner2c/jec_lfoot_dam.erf 2667B ++A mechs/jenner2c/jec_lftoe.erf 3599B ++A mechs/jenner2c/jec_lftoe_dam.erf 3599B ++A mechs/jenner2c/jec_lgun.erf 5943B ++A mechs/jenner2c/jec_lgun_dam.erf 5847B ++A mechs/jenner2c/jec_luleg.erf 13003B ++A mechs/jenner2c/jec_luleg_dam.erf 13003B ++A mechs/jenner2c/jec_rbtoe.erf 3191B ++A mechs/jenner2c/jec_rbtoe_dam.erf 3191B ++A mechs/jenner2c/jec_rdleg.erf 8438B ++A mechs/jenner2c/jec_rdleg_dam.erf 8438B ++A mechs/jenner2c/jec_rfoot.erf 2667B ++A mechs/jenner2c/jec_rfoot_dam.erf 2667B ++A mechs/jenner2c/jec_rftoe.erf 3599B ++A mechs/jenner2c/jec_rftoe_dam.erf 3599B ++A mechs/jenner2c/jec_rgun.erf 5943B ++A mechs/jenner2c/jec_rgun_dam.erf 6287B ++A mechs/jenner2c/jec_ruleg.erf 13003B ++A mechs/jenner2c/jec_ruleg_dam.erf 13003B ++A mechs/jenner2c/jec_torso.erf 39244B ++A mechs/jenner2c/jec_torso_dam.erf 39244B ++A mechs/jenner2c/jenner_2c_cage.erf 13103B ++A mechs/jenner2c/runninglights.erf 3062B ++A mechs/jenner2c_destroyed/jenner2c_destroyed.erf 11866B ++A mechs/marauder/mar_hip.erf 22691B ++A mechs/marauder/mar_hip_dam.erf 6571B ++A mechs/marauder/mar_lbtoe.erf 7962B ++A mechs/marauder/mar_ldleg.erf 39714B ++A mechs/marauder/mar_ldleg_dam.erf 11612B ++A mechs/marauder/mar_lfoot.erf 9492B ++A mechs/marauder/mar_lfoot_dam.erf 2755B ++A mechs/marauder/mar_lgun.erf 18985B ++A mechs/marauder/mar_litoe.erf 4394B ++A mechs/marauder/mar_litoe_dam.erf 1345B ++A mechs/marauder/mar_lotoe.erf 4394B ++A mechs/marauder/mar_luarm.erf 11161B ++A mechs/marauder/mar_luarm_dam.erf 9173B ++A mechs/marauder/mar_luleg.erf 8672B ++A mechs/marauder/mar_luleg_dam.erf 2499B ++A mechs/marauder/mar_rbtoe.erf 7962B ++A mechs/marauder/mar_rdleg.erf 38879B ++A mechs/marauder/mar_rdleg_dam.erf 11676B ++A mechs/marauder/mar_rfoot.erf 9594B ++A mechs/marauder/mar_rfoot_dam.erf 2755B ++A mechs/marauder/mar_rgun.erf 18341B ++A mechs/marauder/mar_ritoe.erf 4394B ++A mechs/marauder/mar_ritoe_dam.erf 1345B ++A mechs/marauder/mar_rotoe.erf 4394B ++A mechs/marauder/mar_rotoe_dam.erf 1345B ++A mechs/marauder/mar_ruarm.erf 11008B ++A mechs/marauder/mar_ruarm_dam.erf 5875B ++A mechs/marauder/mar_ruleg.erf 8672B ++A mechs/marauder/mar_ruleg_dam.erf 2499B ++A mechs/marauder/mar_specialone.erf 21560B ++A mechs/marauder/mar_specialone_dam.erf 7325B ++A mechs/marauder/mar_specialtwo.erf 15696B ++A mechs/marauder/mar_specialtwo_dam.erf 4731B ++A mechs/marauder/mar_torso.erf 54047B ++A mechs/marauder/mar_torso_dam.erf 13479B ++A mechs/marauder/marauder_cage.erf 22678B ++A mechs/marauder/runninglights.erf 541B ++A mechs/marauder_destroyed/marauder_destroyed.erf 18503B ++A mechs/thunderbolt/runninglights.erf 3062B ++A mechs/thunderbolt/thu_hip.erf 5811B ++A mechs/thunderbolt/thu_hip_dam.erf 5811B ++A mechs/thunderbolt/thu_lbtoe.erf 2723B ++A mechs/thunderbolt/thu_ldleg.erf 9171B ++A mechs/thunderbolt/thu_ldleg_dam.erf 9171B ++A mechs/thunderbolt/thu_lfoot.erf 1441B ++A mechs/thunderbolt/thu_lfoot_dam.erf 1441B ++A mechs/thunderbolt/thu_lftoe.erf 2555B ++A mechs/thunderbolt/thu_lftoe_dam.erf 2555B ++A mechs/thunderbolt/thu_lgun.erf 10687B ++A mechs/thunderbolt/thu_luarm.erf 2569B ++A mechs/thunderbolt/thu_luarm_dam.erf 2569B ++A mechs/thunderbolt/thu_luleg.erf 6794B ++A mechs/thunderbolt/thu_luleg_dam.erf 6794B ++A mechs/thunderbolt/thu_rbtoe.erf 2723B ++A mechs/thunderbolt/thu_rbtoe_dam.erf 2723B ++A mechs/thunderbolt/thu_rdleg.erf 9171B ++A mechs/thunderbolt/thu_rdleg_dam.erf 9171B ++A mechs/thunderbolt/thu_rfoot.erf 1441B ++A mechs/thunderbolt/thu_rfoot_dam.erf 1441B ++A mechs/thunderbolt/thu_rftoe.erf 2555B ++A mechs/thunderbolt/thu_rftoe_dam.erf 2555B ++A mechs/thunderbolt/thu_rgun.erf 12097B ++A mechs/thunderbolt/thu_rgun_dam.erf 12097B ++A mechs/thunderbolt/thu_ruarm.erf 2569B ++A mechs/thunderbolt/thu_ruarm_dam.erf 2569B ++A mechs/thunderbolt/thu_ruleg.erf 6794B ++A mechs/thunderbolt/thu_ruleg_dam.erf 6794B ++A mechs/thunderbolt/thu_specialone.erf 4371B ++A mechs/thunderbolt/thu_torso.erf 12695B ++A mechs/thunderbolt/thu_torso_dam.erf 12695B ++A mechs/thunderbolt/thunderbolt_cage.erf 20919B ++A mechs/thunderbolt_destroyed/thunderbolt_destroyed.erf 12593B ++A textures/@achp0.tga 1048594B ++A textures/@achp0.tga{hint} 4B ++A textures/@achp1.tga 262162B ++A textures/@achp1.tga{hint} 4B ++A textures/@achp2.tga 65554B ++A textures/@achp2.tga{hint} 4B ++A textures/@achp3.tga 16402B ++A textures/@achp3.tga{hint} 4B ++A textures/@achp4.tga 4114B ++A textures/@achp4.tga{hint} 4B ++A textures/@achp5.tga 1042B ++A textures/@achp5.tga{hint} 4B ++A textures/@agrf0.tga 1048594B ++A textures/@agrf0.tga{hint} 4B ++A textures/@agrf1.tga 262162B ++A textures/@agrf1.tga{hint} 4B ++A textures/@agrf2.tga 65554B ++A textures/@agrf2.tga{hint} 4B ++A textures/@agrf3.tga 16402B ++A textures/@agrf3.tga{hint} 4B ++A textures/@agrf4.tga 4114B ++A textures/@agrf4.tga{hint} 4B ++A textures/@agrf5.tga 1042B ++A textures/@agrf5.tga{hint} 4B ++A textures/@ajec0.tga 1048594B ++A textures/@ajec0.tga{hint} 4B ++A textures/@ajec1.tga 262162B ++A textures/@ajec1.tga{hint} 4B ++A textures/@ajec2.tga 65554B ++A textures/@ajec2.tga{hint} 4B ++A textures/@ajec3.tga 16402B ++A textures/@ajec3.tga{hint} 4B ++A textures/@ajec4.tga 4114B ++A textures/@ajec4.tga{hint} 4B ++A textures/@ajec5.tga 1042B ++A textures/@ajec5.tga{hint} 4B ++A textures/@amar0.tga 926350B ++A textures/@amar0.tga{hint} 4B ++A textures/@amar1.tga 247836B ++A textures/@amar1.tga{hint} 4B ++A textures/@amar2.tga 66075B ++A textures/@amar2.tga{hint} 4B ++A textures/@amar3.tga 16923B ++A textures/@amar3.tga{hint} 4B ++A textures/@amar4.tga 4635B ++A textures/@amar4.tga{hint} 4B ++A textures/@amar5.tga 1563B ++A textures/@amar5.tga{hint} 4B ++A textures/@athu0.tga 1048594B ++A textures/@athu0.tga{hint} 4B ++A textures/@athu1.tga 262162B ++A textures/@athu1.tga{hint} 4B ++A textures/@athu2.tga 65554B ++A textures/@athu2.tga{hint} 4B ++A textures/@athu3.tga 16402B ++A textures/@athu3.tga{hint} 4B ++A textures/@athu4.tga 4114B ++A textures/@athu4.tga{hint} 4B ++A textures/@athu5.tga 1042B ++A textures/@athu5.tga{hint} 4B ++A textures/footsteps/champion_default.tga 16428B ++A textures/footsteps/champion_default.tga{hint} 4B ++A textures/footsteps/champion_dirt.tga 16923B ++A textures/footsteps/champion_dirt.tga{hint} 4B ++A textures/footsteps/champion_snow.tga 16923B ++A textures/footsteps/champion_snow.tga{hint} 4B ++A textures/footsteps/griffin_default.tga 16428B ++A textures/footsteps/griffin_default.tga{hint} 4B ++A textures/footsteps/griffin_dirt.tga 16923B ++A textures/footsteps/griffin_dirt.tga{hint} 4B ++A textures/footsteps/griffin_snow.tga 16923B ++A textures/footsteps/griffin_snow.tga{hint} 4B ++A textures/footsteps/jenner2c_default.tga 16428B ++A textures/footsteps/jenner2c_default.tga{hint} 4B ++A textures/footsteps/jenner2c_dirt.tga 16428B ++A textures/footsteps/jenner2c_dirt.tga{hint} 4B ++A textures/footsteps/jenner2c_snow.tga 16428B ++A textures/footsteps/jenner2c_snow.tga{hint} 4B ++A textures/footsteps/marauder_default.tga 16923B ++A textures/footsteps/marauder_default.tga{hint} 4B ++A textures/footsteps/marauder_dirt.tga 16923B ++A textures/footsteps/marauder_dirt.tga{hint} 4B ++A textures/footsteps/marauder_snow.tga 16923B ++A textures/footsteps/marauder_snow.tga{hint} 4B ++A textures/footsteps/thunderbolt_default.tga 16428B ++A textures/footsteps/thunderbolt_default.tga{hint} 4B ++A textures/footsteps/thunderbolt_dirt.tga 16428B ++A textures/footsteps/thunderbolt_dirt.tga{hint} 4B ++A textures/footsteps/thunderbolt_snow.tga 16428B ++A textures/footsteps/thunderbolt_snow.tga{hint} 4B +~ textures/stockdecals/decal_49.tga A=16428B B=16428B diff --git a/MW4COMPARE/reports/variants-inventory.txt b/MW4COMPARE/reports/variants-inventory.txt new file mode 100644 index 00000000..3db73e09 --- /dev/null +++ b/MW4COMPARE/reports/variants-inventory.txt @@ -0,0 +1,81 @@ +# variant packages: 797 (files on disk: 797) +# chassis defined in V4H core.mw4 : 71 +# chassis defined in OUR core.mw4 : 65 +# chassis only V4H defines : ['champion', 'dasher', 'griffin', 'jenner_2c', 'marauder', 'thunderbolt'] + +chassis count status +warhammer 36 loadable in OUR build today +rifleman 30 loadable in OUR build today +nova 24 loadable in OUR build today +atlas 23 loadable in OUR build today +madcat 23 loadable in OUR build today +thunderbolt 22 needs the new chassis ported +archer 21 loadable in OUR build today +battlemaster 21 loadable in OUR build today +hunchback 21 loadable in OUR build today +thor 20 loadable in OUR build today +vulture 19 loadable in OUR build today +zeus 19 loadable in OUR build today +ryoken 18 loadable in OUR build today +shadowcat 18 loadable in OUR build today +catapult 17 loadable in OUR build today +dragon 17 loadable in OUR build today +griffin 15 needs the new chassis ported +loki 15 loadable in OUR build today +marauder 15 needs the new chassis ported +commando 13 loadable in OUR build today +highlander 13 loadable in OUR build today +templar 13 loadable in OUR build today +wolfhound 13 loadable in OUR build today +avatar 12 loadable in OUR build today +awesome 12 loadable in OUR build today +battlemaster2c 12 loadable in OUR build today +victor 12 loadable in OUR build today +annihilator 11 loadable in OUR build today +daishi 11 loadable in OUR build today +gladiator 11 loadable in OUR build today +puma 11 loadable in OUR build today +raven 11 loadable in OUR build today +sunder 11 loadable in OUR build today +uller 11 loadable in OUR build today +dasher 10 needs the new chassis ported +owens 10 loadable in OUR build today +blacklanner 9 loadable in OUR build today +cougar 9 loadable in OUR build today +kodiak 9 loadable in OUR build today +masakari 9 loadable in OUR build today +urbanmech 9 loadable in OUR build today +arcticwolf 8 loadable in OUR build today +blacknight 8 loadable in OUR build today +cauldronborn 8 loadable in OUR build today +hellhound 8 loadable in OUR build today +hollander 8 loadable in OUR build today +longbow 8 loadable in OUR build today +novacat 8 loadable in OUR build today +assassin2 7 loadable in OUR build today +cyclops 7 loadable in OUR build today +flea 7 loadable in OUR build today +hauptmann 7 loadable in OUR build today +mauler 7 loadable in OUR build today +behemoth2 6 loadable in OUR build today +brigand 6 loadable in OUR build today +bushwacker 6 loadable in OUR build today +champion 5 needs the new chassis ported +deimos 5 loadable in OUR build today +hellspawn 5 loadable in OUR build today +madcat_mkii 5 loadable in OUR build today +thanatos 5 loadable in OUR build today +uziel 5 loadable in OUR build today +argus 4 loadable in OUR build today +fafnir 4 loadable in OUR build today +jenner_iic1 3 BROKEN - chassis missing from V4H too +osiris 3 loadable in OUR build today +behemoth 2 loadable in OUR build today +chimera 2 loadable in OUR build today +grizzly 2 loadable in OUR build today +solitaire 2 loadable in OUR build today + +loadable in our build today : 727 +blocked on new chassis : 67 +broken / orphaned : 3 + orphan chassis 'jenner_iic1': Variants/Jenner2c 1.mw4, Variants/Jenner2c 4.mw4, Variants/Jenner2c Pharaoh.mw4 diff --git a/MW4COMPARE/run-comparison.sh b/MW4COMPARE/run-comparison.sh new file mode 100755 index 00000000..645327a5 --- /dev/null +++ b/MW4COMPARE/run-comparison.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# run-comparison.sh - regenerate every report in MW4COMPARE/reports/. +# +# ./run-comparison.sh [V4H_ROOT] [OURS_ROOT] +# +# Defaults: +# V4H_ROOT = /home/rich/Repositories/FS_Build_V4H (deployed 3rd-party build) +# OURS_ROOT = /Gameleap/mw4 (our dev/source tree) +# +# Takes a few minutes; the decoded props/textures comparisons dominate. +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +T="$HERE/tools" +R="$HERE/reports" +V4H="${1:-/home/rich/Repositories/FS_Build_V4H}" +OURS="${2:-$(cd "$HERE/.." && pwd)/Gameleap/mw4}" + +mkdir -p "$R" +echo "V4H = $V4H" +echo "OURS = $OURS" + +echo "[1/8] package manifests" +python3 "$T/mw4index.py" "$V4H/resource" > "$R/manifest-v4h.tsv" 2> "$R/manifest-v4h.err" +python3 "$T/mw4index.py" "$OURS/Resource" > "$R/manifest-ours.tsv" 2> "$R/manifest-ours.err" + +echo "[2/8] package + entry name diff" +python3 "$T/diffindex.py" "$R/manifest-v4h.tsv" "$R/manifest-ours.tsv" V4H OURS \ + > "$R/packages-entrydiff.txt" + +echo "[3/8] core.mw4 decoded diff" +python3 "$T/pkgcmp.py" "$V4H/resource/core.mw4" "$OURS/Resource/core.mw4" \ + > "$R/core-decoded-diff.txt" + +echo "[4/8] props.mw4 decoded diff (~1 min)" +python3 "$T/pkgcmp.py" "$V4H/resource/props.mw4" "$OURS/Resource/props.mw4" \ + > "$R/props-decoded-diff.txt" + +echo "[5/8] textures.mw4 decoded diff (~2 min)" +python3 "$T/pkgcmp.py" "$V4H/resource/textures.mw4" "$OURS/Resource/textures.mw4" \ + > "$R/textures-decoded-diff.txt" + +echo "[6/8] per-mission and per-map decoded diffs" +: > "$R/missions-decoded-diff.txt" +for f in "$V4H"/resource/Missions/*.mw4; do + b="$(basename "$f")"; o="$OURS/Resource/Missions/$b" + [ -f "$o" ] || continue + { echo "### $b"; python3 "$T/pkgcmp.py" "$f" "$o"; } >> "$R/missions-decoded-diff.txt" +done +: > "$R/maps-decoded-diff.txt" +for f in "$V4H"/resource/maps/*.mw4; do + b="$(basename "$f")"; o="$OURS/Resource/maps/$b" + [ -f "$o" ] || continue + { echo "### $b"; python3 "$T/pkgcmp.py" "$f" "$o"; } >> "$R/maps-decoded-diff.txt" +done + +echo "[7/8] loose file trees" +python3 "$T/treediff.py" "$V4H/hsh" "$OURS/hsh" V4H OURS > "$R/hsh-diff.txt" +python3 "$T/treediff.py" "$V4H/hshoriginal" "$OURS/hsh" V4HORIG OURS > "$R/hshoriginal-diff.txt" +python3 "$T/treediff.py" "$V4H/resource" "$OURS/Resource" V4H OURS > "$R/resource-files-diff.txt" +# V4H ships only 14 loose Content files against our ~46k-file source tree, so the +# "-OURS" side is pure noise here; keep only the header, additions and conflicts. +python3 "$T/treediff.py" "$V4H/content" "$OURS/Content" V4H OURS \ + | grep -v '^-OURS' > "$R/content-diff.txt" + +echo "[8/8] variant inventory" +python3 "$T/variants-report.py" "$V4H/resource/Variants" "$R/manifest-v4h.tsv" "$R/manifest-ours.tsv" \ + > "$R/variants-inventory.txt" + +echo "done -> $R" diff --git a/MW4COMPARE/tools/.gitignore b/MW4COMPARE/tools/.gitignore new file mode 100644 index 00000000..c18dd8d8 --- /dev/null +++ b/MW4COMPARE/tools/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/MW4COMPARE/tools/classify-survivors.py b/MW4COMPARE/tools/classify-survivors.py new file mode 100644 index 00000000..8b7f1257 --- /dev/null +++ b/MW4COMPARE/tools/classify-survivors.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +""" +classify-survivors.py - split a pruned extracted tree into NEW vs DIFFERS. + + python3 classify-survivors.py + +Run after prune-identical.py. Everything still present is something we do not +have byte-for-byte; this reports which of two reasons applies: + + NEW no file at that entry path exists on our side at all + DIFFERS we have that entry path, but the bytes differ + +"Our side" means our extracted packages OR our Content/ source tree, matched +case-insensitively - the same notion of "have it" the pruner uses. + +Writes _classified.tsv next to the tree and prints a summary. +""" +import sys, os, collections + +OURS_EXTRACTED = "/home/rich/Repositories/FS_Ours_extracted" +OUR_SOURCE = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content" + + +def index_paths(root, skip_top=()): + out = {} + for dp, dirs, fs in os.walk(root): + if dp == root: + dirs[:] = [d for d in dirs if d.lower() not in skip_top] + for f in fs: + p = os.path.join(dp, f) + out[os.path.relpath(p, root).replace("\\", "/").lower()] = p + return out + + +def package_of(rel): + parts = rel.split("/") + head = parts[0].lower() + if head in ("core", "props", "textures"): + return parts[0], "/".join(parts[1:]) + if head in ("maps", "missions", "variants") and len(parts) > 2: + return "/".join(parts[:2]), "/".join(parts[2:]) + if head == "pilots" and len(parts) > 3: + return "/".join(parts[:3]), "/".join(parts[3:]) + return parts[0], "/".join(parts[1:]) + + +def main(): + if len(sys.argv) < 2: + sys.exit(__doc__) + root = sys.argv[1] + merged = os.path.join(root, "_merged") + + ours = index_paths(OURS_EXTRACTED, skip_top={"_merged"}) + ours_entries = set() + for rel in ours: + ours_entries.add(package_of(rel)[1]) + src_entries = set(index_paths(OUR_SOURCE)) + + rows, summary = [], collections.Counter() + for dp, _dirs, fs in os.walk(root): + if dp == merged or dp.startswith(merged + os.sep): + continue + for f in fs: + rel = os.path.relpath(os.path.join(dp, f), root).replace("\\", "/") + if rel.startswith("_"): + continue + pkg, entry = package_of(rel) + e = entry.lower() + verdict = "DIFFERS" if (rel.lower() in ours or e in ours_entries + or e in src_entries) else "NEW" + rows.append((verdict, rel, pkg, entry)) + summary[(verdict, pkg.split("/")[0])] += 1 + + with open(os.path.join(root, "_classified.tsv"), "w", encoding="utf-8") as fh: + fh.write("verdict\tpath\tpackage\tentry\n") + for r in sorted(rows): + fh.write("\t".join(r) + "\n") + + tot = collections.Counter(v for v, _, _, _ in rows) + print(f"survivors: {len(rows)} NEW={tot['NEW']} DIFFERS={tot['DIFFERS']}\n") + print(f"{'package group':16s} {'NEW':>7s} {'DIFFERS':>8s}") + groups = sorted({g for _v, g in summary}) + for g in groups: + print(f"{g:16s} {summary[('NEW', g)]:7d} {summary[('DIFFERS', g)]:8d}") + print(f"\n-> {os.path.join(root, '_classified.tsv')}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/armature.py b/MW4COMPARE/tools/decompile/armature.py new file mode 100644 index 00000000..a3266d8b --- /dev/null +++ b/MW4COMPARE/tools/decompile/armature.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +""" +armature.py - rebuild a mech's .armature source from its packed records. + + python3 armature.py [-o out.armature] + python3 armature.py --verify # check against all 65 known chassis + +The packer merges .armature into .contents (via `!include=`) and +then, for every contents page that has Child= entries, emits two records +(MWMover_Tool.cpp ~78): + + .contents[]{sites} children whose name starts 'site_' + but is not 'site_eye*' - stored as + YawPitchRoll + Point3D + name + .contents[]{armature} every other child, as a CreateMessage + carrying jointName + localToParent + +Between them those two records hold every page of the original .armature. +Verified on Atlas: 61 of 61 page names recovered, values exact. + +Rotation notes +-------------- +* {sites} stores YawPitchRoll directly, so those angles are exact. +* Joint messages only carry a matrix. Across all 65 chassis, 1348 of 1453 joint + matrices are the identity, i.e. `Rotation=0 0 0`. +* site_lfoot / site_rfoot appear in BOTH streams - a deliberate hack in + MWMover_Tool.cpp ("Jerry this will get deleted when you fix your foot + problem") - and their matrix is the identity while the real angle is in the + {sites} record. The {sites} value always wins. +* site_eye* goes only to the armature stream, so its angle comes from the + matrix. +""" +import sys, os, glob, math, struct, re, argparse, collections + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import mw4msg + +OUR_MECH_SOURCE = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Mechs" +OUR_RECORDS = "/home/rich/Repositories/FS_Ours_extracted/core/mechs" + +REC_RE = re.compile(r'^(?P.+?)\.contents\[(?P[^\]]+)\]\{(?Parmature|sites)\}$', + re.I) + + +def ypr_from_matrix(r): + """3x3 -> the (a, b, c) triple as written in Rotation=, in degrees. + + Stuff's YawPitchRoll writes the X-axis angle in the middle slot; confirmed + against kodiak/site_eyepoint (matrix gives 0.0688 deg, source says + 0.069104). + """ + m00, m01, m02, m10, m11, m12, m20, m21, m22 = r + pitch = math.asin(max(-1.0, min(1.0, m21))) + if abs(m21) < 0.999999: + yaw = math.atan2(-m20, m22) + roll = math.atan2(-m01, m11) + else: + yaw = math.atan2(m02, m00) + roll = 0.0 + return (math.degrees(yaw), math.degrees(pitch), math.degrees(roll)) + + +def is_identity(r, eps=1e-5): + ident = (1, 0, 0, 0, 1, 0, 0, 0, 1) + return all(abs(a - b) < eps for a, b in zip(r, ident)) + + +def rebuild(mech_dir): + """-> (chassisName, entries, children) + + entries list of {'name','parent','rot','trans'} - one per armature page. + Keyed on (parent, name), not name alone: Victor legitimately has + two different [site_lshellport] pages hanging off different joints. + children {parentName: [childName, ...]} preserving order and multiplicity. + """ + entries = [] # one dict per armature page occurrence + children = collections.defaultdict(list) + site_children = collections.defaultdict(set) + chassis = None + # {sites} first: site_lfoot/site_rfoot are written to BOTH streams and only + # the {sites} copy carries their real angle. + records = (sorted(glob.glob(os.path.join(mech_dir, "*{sites}"))) + + sorted(glob.glob(os.path.join(mech_dir, "*{armature}")))) + for path in records: + m = REC_RE.match(os.path.basename(path)) + if not m: + continue + chassis = chassis or m.group("chassis") + parent, kind = m.group("parent"), m.group("kind").lower() + with open(path, "rb") as fh: + data = fh.read() + children[parent] # ensure the parent exists + + if kind == "sites": + for name, rot, trans in mw4msg.read_sites(data): + entries.append({ + "name": name, "parent": parent, + "rot": tuple(math.degrees(a) for a in rot), "trans": trans}) + children[parent].append(name) + site_children[parent].add(name) + else: + for _off, msg in mw4msg.walk(data): + name = mw4msg.joint_name(msg) + if not name: + continue + # site_lfoot/site_rfoot are deliberately written to both streams; + # counting the armature copy too would double them. + if name in site_children[parent]: + continue + children[parent].append(name) + r = mw4msg.rotation3x3(msg) + entries.append({ + "name": name, "parent": parent, + "rot": (0.0, 0.0, 0.0) if is_identity(r) else ypr_from_matrix(r), + "trans": mw4msg.translation(msg)}) + return chassis, entries, children + + +def emit(entries, children): + """Render as .armature text, children before the joint that owns them.""" + by_name = {} + for e in entries: + by_name.setdefault(e["name"], []).append(e) + out, done = [], set() + + def visit(name): + if name in done: + return + done.add(name) + for c in children.get(name, ()): + visit(c) + for e in by_name.get(name, [{"rot": (0.0, 0.0, 0.0), "trans": (0.0, 0.0, 0.0)}]): + out.append(f"[{name}]") + out.append("Rotation=%.6f %.6f %.6f" % (e["rot"] or (0.0, 0.0, 0.0))) + out.append("Translation=%.6f %.6f %.6f" % (e["trans"] or (0.0, 0.0, 0.0))) + for c in children.get(name, ()): + out.append(f"Child={c}") + out.append("") + + all_children = {c for lst in children.values() for c in lst} + for name in list(children) + list(by_name): + if name not in all_children: + visit(name) + for name in list(children) + list(by_name): + visit(name) + return "\r\n".join(out) + "\r\n" + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("mechdir", nargs="?", help="directory of packed records for one mech") + ap.add_argument("-o", "--out", help="write .armature here instead of stdout") + ap.add_argument("--verify", action="store_true", + help="rebuild all 65 known chassis and diff against their source") + args = ap.parse_args() + + if args.verify: + here = os.path.dirname(os.path.abspath(__file__)) + os.execvp("python3", ["python3", os.path.join(here, "verify_armature.py")]) + if not args.mechdir: + ap.error("give a mech record directory, or --verify") + + chassis, entries, children = rebuild(args.mechdir) + text = emit(entries, children) + if args.out: + os.makedirs(os.path.dirname(args.out), exist_ok=True) + open(args.out, "wb").write(text.encode("latin-1")) + print(f"{chassis}: {len(entries)} pages -> {args.out}") + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/armature_parts.py b/MW4COMPARE/tools/decompile/armature_parts.py new file mode 100644 index 00000000..7a30b3d6 --- /dev/null +++ b/MW4COMPARE/tools/decompile/armature_parts.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +""" +armature_parts.py - generate a mech's `armaturedata/` and `armaturevideo/` files. + +Every joint in a mech's `.contents` points at `armaturedata\\.data`, which +in turn points at `armaturevideo\\.video`, which names the geometry. Both +are packed as compiled records -- the `.data` becomes a 12-byte stub and the +`.video` becomes a binary renderer graph -- so neither survives extraction as +text and both have to be regenerated. + +They are formulaic. Measured over the 89 shipped mechs: + + * `armaturedata/.data` -- **one single shape** across all 1336 files, + parameterised only by the part name. + * `armaturevideo/.video` -- three shapes cover 91% of 1354 files: + with a damaged LOD, without one, and with a running-lights block. + +Rules, and their measured accuracy: + + * A `DAMLOD` block iff `_dam.erf` exists next to the mech. This holds for + 1327 of 1354 shipped files. **All 27 exceptions run the same way**: the + `_dam.erf` exists but the author's `.video` ignores it. No shipped `.video` + ever references a `_dam.erf` that is absent, so generating by this rule is + safe by construction -- it can never produce a dangling reference, only + occasionally more damage-state switching than the original author chose. + The exceptions are mostly `_gun` and `_toe` parts and follow no clean rule. + * A running-lights block iff the part is the torso and a running-lights `.erf` + is present. 68 of the 71 torsos carry it; almost nothing else does. + + python3 armature_parts.py + python3 armature_parts.py --verify +""" +import argparse, os, re, sys, collections + +DATA_TEMPLATE = """[gamedata] +Class=MechWarrior4::MWMover + +[renderers] +VideoRenderer=armaturevideo\\{part}.video +""" + +LIGHT_BLOCK = """[lightlod] +Type=ShapeComponent +Geometry={lights} +Unique=true + +[lightwatcher] +Type=AttributeWatcherOfInt +Attribute=IsDark +SimulationShouldExecute=1 + +[lightswitch] +Type=SwitchComponent +Input=LightWatcher +Child=LightLOD + +""" + +LOD_BLOCK = """[lod] +Type=ShapeComponent +Geometry={part}.erf +Unique=true + +""" + +DAM_BLOCK = """[damlod] +Type=ShapeComponent +Geometry={part}_DAM.erf +Unique=true + +""" + +# joint_cage has its own shape: two empty group components, then the cage +# geometry twice (intact and destroyed), shadow-disabled. +CAGE_TEMPLATE = """[fake1] +Type=GroupComponent + +[fake2] +Type=GroupComponent + +[cage] +Type=ShapeComponent +Geometry={cage} +Unique=true +DisableShadow=true + +[destroyedcage] +Type=ShapeComponent +Geometry={cage} +Unique=true +DisableShadow=true + +[watcher] +Type=AttributeWatcherOfInt +Attribute=VisualRepresentation +SimulationShouldExecute=1 + +[damageappearance] +Type=SwitchComponent +Input=Watcher +Child=Fake1 +Child=Fake2 +Child=Cage +Child=DestroyedCage + +[locator] +Child=DamageAppearance +""" + + +def parts_of(mech_dir): + """Part names referenced as armaturedata\\.data by the .contents.""" + out = [] + for fn in os.listdir(mech_dir): + if not fn.lower().endswith(".contents"): + continue + t = open(os.path.join(mech_dir, fn), "rb").read().decode("latin-1") + for m in re.finditer(r'armaturedata[\\/]([A-Za-z0-9_\-]+)\.data', t, re.I): + if m.group(1) not in out: + out.append(m.group(1)) + return out + + +def video_text(mech_dir, part): + files = {f.lower(): f for f in os.listdir(mech_dir)} + cage = next((files[f] for f in files if f.endswith("_cage.erf")), None) + if part.lower() == "joint_cage" and cage: + return CAGE_TEMPLATE.format(cage=cage) + + has_dam = f"{part.lower()}_dam.erf" in files + lights = next((files[f] for f in files + if f.startswith("runninglights") and f.endswith(".erf")), None) + is_torso = part.lower().endswith("torso") + + text = "" + if lights and is_torso: + text += LIGHT_BLOCK.format(lights=lights) + text += LOD_BLOCK.format(part=part) + if has_dam: + text += DAM_BLOCK.format(part=part) + text += """[watcher] +Type=AttributeWatcherOfInt +Attribute=VisualRepresentation +SimulationShouldExecute=1 + +[damageappearance] +Type=SwitchComponent +Input=Watcher +Child=LOD +""" + if has_dam: + text += "Child=DAMLOD\n" + text += "\n[locator]\n" + if lights and is_torso: + text += "Child=LightSwitch\n" + text += "Child=DamageAppearance\n" + return text + + +def generate(mech_dir, write=True): + """-> {relativePath: text} for every part.""" + out = {} + for part in parts_of(mech_dir): + out[f"armaturedata/{part}.data"] = DATA_TEMPLATE.format(part=part) + out[f"armaturevideo/{part}.video"] = video_text(mech_dir, part) + if write: + for rel, text in out.items(): + p = os.path.join(mech_dir, rel) + os.makedirs(os.path.dirname(p), exist_ok=True) + with open(p, "wb") as fh: + fh.write(text.replace("\n", "\r\n").encode("latin-1")) + return out + + +def norm(text, part): + t = re.sub(r'//[^\n]*', '', text).replace("\r", "") + t = re.sub(re.escape(part), "PART", t, flags=re.I) + return re.sub(r'\n+', "\n", t).strip().lower() + + +def verify(): + import glob + MECHS = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Mechs" + t = collections.Counter() + diffs = collections.Counter() + for d in sorted(glob.glob(MECHS + "/*")): + if not os.path.isdir(d) or not glob.glob(d + "/armaturedata/*.data"): + continue + gen = generate(d, write=False) + for rel, text in gen.items(): + p = os.path.join(d, rel) + part = os.path.basename(rel).rsplit(".", 1)[0] + kind = rel.split("/")[0] + if not os.path.exists(p): + t[kind + " absent"] += 1 + continue + t[kind] += 1 + want = open(p, "rb").read().decode("latin-1") + if norm(want, part) == norm(text, part): + t[kind + " ok"] += 1 + else: + diffs[kind] += 1 + print(f"armaturedata : {t['armaturedata ok']}/{t['armaturedata']} reproduced") + print(f"armaturevideo: {t['armaturevideo ok']}/{t['armaturevideo']} reproduced") + if t["armaturedata absent"] or t["armaturevideo absent"]: + print(f" referenced but absent on disk: " + f"{t['armaturedata absent']} data, {t['armaturevideo absent']} video") + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("mech_dir", nargs="?") + ap.add_argument("--verify", action="store_true") + args = ap.parse_args() + if args.verify: + verify() + return + if not args.mech_dir: + ap.error("mech_dir required") + out = generate(args.mech_dir) + print(f"wrote {len(out)} files into {args.mech_dir}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/assembly.py b/MW4COMPARE/tools/decompile/assembly.py new file mode 100644 index 00000000..57ca1ecb --- /dev/null +++ b/MW4COMPARE/tools/decompile/assembly.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +""" +assembly.py - show how a mech's parts bolt together. + +There is no `.erf` viewer on Linux, but the assembly itself is not hidden: the +`.armature` gives every joint's parent, offset and rotation, and the `.contents` +plus `armaturevideo/` say which geometry hangs off each joint. Printing that as +a tree answers "what are the parts and how do they fit" without a renderer. + +For actual 3D you need MW4Ed2 on the Windows box (Gameleap/mw4/run-editor.bat) -- +its Game View renders a loaded mech through DDrawCompat. + + python3 assembly.py [--geometry-only] +""" +import argparse, collections, os, re, sys + + +def pages(path): + """-> OrderedDict pageName -> [(key, value)]""" + txt = re.sub(r'//[^\n]*', '', open(path, "rb").read().decode("latin-1")) + out, cur = collections.OrderedDict(), None + for line in txt.splitlines(): + line = line.strip() + if not line or line.startswith("!"): + continue + m = re.match(r'^\[([^\]]+)\]$', line) + if m: + cur = m.group(1) + out.setdefault(cur, []) + elif "=" in line and cur is not None: + k, v = line.split("=", 1) + out[cur].append((k.strip(), v.strip())) + return out + + +def find(mech_dir, ext): + for f in sorted(os.listdir(mech_dir)): + if f.lower().endswith(ext) and "}" not in f: + return os.path.join(mech_dir, f) + return None + + +def geometry_for(mech_dir, part): + """The .erf a joint draws, via armaturevideo/.video. + + Takes the [lod] block specifically -- a torso's file starts with the + running-lights block, whose geometry is not the part. + """ + vid = os.path.join(mech_dir, "armaturevideo", part + ".video") + if not os.path.exists(vid): + return None + t = open(vid, "rb").read().decode("latin-1") + m = re.search(r'^\[lod\][^\[]*?Geometry=([^\r\n]+)', t, re.M | re.I | re.S) + if not m: + m = re.search(r'Geometry=([^\r\n]+)', t) + return m.group(1).strip() if m else None + + +def build(mech_dir): + arm = find(mech_dir, ".armature") + con = find(mech_dir, ".contents") + if not arm: + raise SystemExit(f"no .armature in {mech_dir}") + + apages = pages(arm) + children = collections.defaultdict(list) + info = {} + for name, kv in apages.items(): + d = dict(kv) + info[name] = d + for k, v in kv: + if k.lower() == "child": + children[name].append(v.strip()) + + models = {} + if con: + for name, kv in pages(con).items(): + for k, v in kv: + if k.lower() == "model": + models[name.lower()] = v.strip() + + parented = {c for cs in children.values() for c in cs} + roots = [n for n in apages if n not in parented] + return apages, children, info, models, roots + + +def render(mech_dir, geometry_only=False): + apages, children, info, models, roots = build(mech_dir) + sizes = {f.lower(): os.path.getsize(os.path.join(mech_dir, f)) + for f in os.listdir(mech_dir) if f.lower().endswith(".erf")} + lines = [] + + def walk(name, depth, last, prefix): + d = info.get(name, {}) + model = models.get(name.lower(), "") + part = None + m = re.match(r'armaturedata[\\/](.+)\.data', model, re.I) + if m: + part = m.group(1) + geo = geometry_for(mech_dir, part) if part else None + size = sizes.get((geo or "").lower()) + tag = "" + if geo: + tag = f" <- {geo}" + (f" ({size:,} B)" if size else "") + elif model and model.lower() != "basic.data": + tag = f" <- {model}" + trans = d.get("Translation", d.get("translation", "")) + rot = d.get("Rotation", d.get("rotation", "")) + pos = f" @[{trans}]" if trans and trans.strip() not in ("0.0 0.0 0.0", "0 0 0") else "" + if geometry_only and not geo: + pass + else: + branch = "" if depth == 0 else ("`-- " if last else "|-- ") + lines.append(f"{prefix}{branch}{name}{tag}{pos}") + kids = children.get(name, []) + newprefix = prefix + ("" if depth == 0 else (" " if last else "| ")) + for i, k in enumerate(kids): + walk(k, depth + 1, i == len(kids) - 1, newprefix) + + for r in roots: + walk(r, 0, True, "") + return lines, sizes + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("mech_dir") + ap.add_argument("--geometry-only", action="store_true", + help="hide joints and sites that draw nothing") + args = ap.parse_args() + lines, sizes = render(args.mech_dir, args.geometry_only) + name = os.path.basename(args.mech_dir.rstrip("/")) + print(f"=== {name}: {len(lines)} nodes, {len(sizes)} .erf, " + f"{sum(sizes.values()):,} B of geometry ===") + print("\n".join(lines)) + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/constants.py b/MW4COMPARE/tools/decompile/constants.py new file mode 100644 index 00000000..71619fce --- /dev/null +++ b/MW4COMPARE/tools/decompile/constants.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +""" +constants.py - reverse tables for the `.data` keys authored as symbolic names. + +Four `[GameData]` keys are written in the source as a symbol rather than a +literal, and the record stores only the resolved integer. Emitting a faithful +`.data` therefore needs the int -> symbol direction: + + MechID $(M_Annihilator) MechLabHeaders.h #define M_Annihilator 0 + TechType $(Tech_IS) MechLabHeaders.h #define Tech_IS 0 + NameIndex $(IDS_Annihilator) MissionLang.defines #define IDS_ANNIHILATOR 576 + MoveTypeFlag LEGJUMPMOVETYPE MWObject.hpp enum + MWObject_Tool.cpp:20 + +MoveTypeFlag is the odd one out: a bare token, not `$(...)`, matched by the +`stricmp` chain in `MWObject__GameModel::ConvertStringToMoveType`. Its values +come from the anonymous enum at MWObject.hpp:136, where LEG is 0 and the order +is NOT the same as the stricmp chain, so the enum is the authority. + +The `.defines` and `.h` files spell symbols in mixed case while sources +reference them in any case, so lookups here are case-insensitive. +""" +import re + +REPO = "/home/rich/Repositories/firestorm/Gameleap" +MECHLAB_HEADERS = REPO + "/mw4/Content/ShellScripts/MechLabHeaders.h" +MISSIONLANG_DEFINES = REPO + "/mw4/Content/Defines/MissionLang.defines" + +# MWObject.hpp:136. Declaration order is the value order. +MOVE_TYPE = [ + "LEGMOVETYPE", "LEGJUMPMOVETYPE", "TRACKMOVETYPE", "WHEELMOVETYPE", + "FLYERMOVETYPE", "HOVERMOVETYPE", "HELIMOVETYPE", "NONEMOVETYPE", + "DROPSHIPMOVETYPE", "WATERMOVETYPE", +] + +_DEFINE = re.compile(r'^\s*#define\s+(\w+)\s+(-?\d+)\s*$', re.M) + + +def defines(path, prefix): + """-> {value: [symbols]} for every `#define NAME ` in a file. + + Values are not unique: IDS_FIRSTSKIN and IDS_WOLFHOUND are both 501, so a + single winner cannot be picked here without knowing which mech is being + emitted. + """ + txt = open(path, encoding="latin-1", errors="replace").read() + out = {} + for name, val in _DEFINE.findall(txt): + if name.lower().startswith(prefix.lower()): + out.setdefault(int(val), []).append(name) + return out + + +def tables(): + """-> {sourceKey: {intValue: [sourceToken]}} for the four symbolic keys.""" + wrap = lambda d: {v: [f"$({s})" for s in syms] for v, syms in d.items()} + return { + "MechID": wrap(defines(MECHLAB_HEADERS, "M_")), + "TechType": wrap(defines(MECHLAB_HEADERS, "Tech_")), + "NameIndex": wrap(defines(MISSIONLANG_DEFINES, "IDS_")), + "MoveTypeFlag": {i: [s] for i, s in enumerate(MOVE_TYPE)}, + } + + +def symbol(tbl, key, value, hint=None): + """Pick the source token for a value, preferring one naming the chassis.""" + syms = tbl.get(key, {}).get(value) + if not syms: + return None + if hint and len(syms) > 1: + h = norm(hint) + for s in syms: + if h and h in norm(s): + return s + return syms[0] + + +def norm(tok): + """Compare symbols ignoring case and $() wrapping.""" + return re.sub(r'[^a-z0-9_]', '', tok.lower()) + + +if __name__ == "__main__": + for key, tbl in tables().items(): + print(f"{key:14s} {len(tbl):5d} values e.g. {list(tbl.items())[:2]}") diff --git a/MW4COMPARE/tools/decompile/contents.py b/MW4COMPARE/tools/decompile/contents.py new file mode 100644 index 00000000..1f7d6ab2 --- /dev/null +++ b/MW4COMPARE/tools/decompile/contents.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +contents.py - decompiler for a mech `.contents` file. + +`.contents` is the thin half of the pair `armature.py` already handles. It +`!include`s `.armature` and then gives every joint and site exactly two +entries: + + [joint_torso] + Model=basic.data + ExecutionState=AlwaysExecuteState + +Both values ride in the same CreateMessages `armature.py` walks -- the `.armature` +source contributes a page's geometry, the `.contents` source contributes its +Model and ExecutionState, and the packer merges them into one message per page. +So nothing new has to be located: `Model` is `dataListID` (record id in the HIGH +word) and `ExecutionState` is the enum at offset 76. + +Site pages are the exception. `{sites}` records store only name, rotation and +translation, so a site's Model and ExecutionState are not in the package at all. +They do not need to be: all **2926** site pages across the 89 mech `.contents` +files carry the identical pair `basic.data` / `AlwaysExecuteState`, so they are +emitted as constants rather than guessed per mech. + + python3 contents.py [-o out.contents] + python3 contents.py --verify +""" +import argparse, collections, glob, os, re, struct, subprocess, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import mw4msg, subsystems + +MANIFEST = "/home/rich/Repositories/FS_Ours_extracted/_manifest.tsv" +REC_RE = re.compile(r'^(?P.+?)\.contents\[(?P[^\]]+)\]\{(?Parmature|sites)\}$', + re.I) +EXEC_STATE = {1: "NeverExecuteState", 2: "AlwaysExecuteState", 6: "ActiveState"} +SITE_DEFAULTS = [("Model", "basic.data"), ("ExecutionState", "AlwaysExecuteState")] + +EXEC_OFF = 76 +DATALIST_OFF = 84 + + +def relative(path, chassis, folder=None): + """Model= is written relative to the mech folder, not from the package root. + + The package path uses the FOLDER name, which need not match the record stem + (V4H's jenner2c/ holds jenner_2c.* records), so both are tried. + """ + if not path: + return path + for name in (chassis, folder): + if name: + path = re.sub(rf'^mechs[\\/]{re.escape(name)}[\\/]', '', path, flags=re.I) + return path + + +def decompile(mech_dir, manifest=None): + """-> (chassis, [(pageName, [(key, value)])])""" + manifest = manifest if manifest is not None else subsystems.load_manifest(MANIFEST) + chassis = None + folder = os.path.basename(mech_dir.rstrip("/")) + pages = collections.OrderedDict() + + root = [p for p in glob.glob(os.path.join(mech_dir, "*.contents")) + if not re.search(r'[\[\{]', os.path.basename(p))] + for path in sorted(glob.glob(os.path.join(mech_dir, "*{armature}"))) + root: + base = os.path.basename(path) + m = REC_RE.match(base) + if m: + chassis = chassis or m.group("chassis") + else: + chassis = chassis or base[:-len(".contents")] + with open(path, "rb") as fh: + data = fh.read() + for _off, msg in mw4msg.walk(data): + name = mw4msg.joint_name(msg) + if not name: + continue + model = manifest.get(mw4msg.record_id(msg, DATALIST_OFF)) + state = EXEC_STATE.get(struct.unpack_from(".damage` file. + +The record is a bare concatenation of variable-length objects with no index and +no length prefixes: parsing means walking forward, reading a classID, and using +it to decide what follows. Written by `MWObject::CreateDamageStream` +(MWObject_Tool.cpp:1149), which iterates the source pages in order and dispatches +on whether a page carries a `DamageZone` entry. + +Armour page -- DamageObject::ConstructDamageObjectStream (DamageObject.cpp:157): + + classID, baseArmorValue, currentArmorValue, scaleSplashDamage, + damageObjectName (MString), internalDamageZoneID, armorZone, damageLevel, + armorType, maxArmorValue, attachedToZone + +Internal page -- InternalDamageObject::ConstructInternalDamageObjectStream +(DamageObject.cpp:931) plus the MW4 subclass (MWDamageObject.cpp:88): + + classID, baseInternalDamage, currentInternalDamage, + parentEntityName (MString), damageMode, damageZone, damagePropagationZone, + internalType, attachedTo, damageEffects[], missileSlots, projectileSlots, + beamSlots, omniSlots + +MString is an int length NOT counting the terminator, the characters, then a NUL +-- the same encoding as the {FootSteps} stream. + +Two traps: + + * The armour page stores its own name, but the internal page does NOT. Internal + page names are reconstructed as `Internal`, which every one of the 89 + mech .damage files follows, using the damageZone that IS stored. + * ArmorZone and InternalZone are DIFFERENT enums. ArmorZone has + CenterRearTorso at 7 and Head at 8; InternalZone has Head at 7 and no rear + torso entry. Conflating them silently mislabels head and torso zones. + + python3 damage.py [-o out.damage] + python3 damage.py --verify +""" +import argparse, collections, os, re, struct, subprocess, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import subsystems + +MANIFEST = "/home/rich/Repositories/FS_Ours_extracted/_manifest.tsv" + +ARMOR_CLASS = 468 # Adept::DamageObject +INTERNAL_CLASS = 1162 # MechWarrior4::MWInternalDamageObject + +# DamageObject.hpp:363 -- note CenterRearTorso, absent from the internal enum. +ARMOR_ZONE = { + -1: "NullZone", 0: "LeftLeg", 1: "RightLeg", 2: "LeftArm", 3: "RightArm", + 4: "RightTorso", 5: "LeftTorso", 6: "CenterTorso", 7: "CenterRearTorso", + 8: "Head", 9: "Special1", 10: "Special2", 11: "DefaultZone", +} +# DamageObject.hpp:204 +INTERNAL_ZONE = { + -1: "NullZone", 0: "LeftLeg", 1: "RightLeg", 2: "LeftArm", 3: "RightArm", + 4: "RightTorso", 5: "LeftTorso", 6: "CenterTorso", 7: "Head", + 8: "Special1", 9: "Special2", 10: "VehicleHull", 11: "VehicleWeapon", + 12: "VehicleSpecial", 13: "DefaultZone", +} +# DamageObject.hpp:185 +DAMAGE_MODE = { + 0: "GeneralDamageMode", 1: "GimpLeftDamageMode", 2: "GimpRightDamageMode", + 3: "DestructionDamageMode", 4: "DetachableDamageMode", 5: "EngineDamageMode", + 6: "NextDamageMode", 7: "HeadShotDamageMode", 8: "GyroHitDamageMode", + 9: "TorsoLeftDamageMode", 10: "TorsoRightDamageMode", + 11: "ArmLeftDamageMode", 12: "ArmRightDamageMode", +} + + +class Reader: + def __init__(self, blob): + self.b, self.o = blob, 0 + + def i32(self): + v = struct.unpack_from("= len(self.b) + + +def parse(blob): + """-> [(kind, {field: value})] in stream order.""" + r = Reader(blob) + out = [] + while not r.done(): + class_id = r.i32() + if class_id == ARMOR_CLASS: + rec = { + "baseArmorValue": r.f32(), + "currentArmorValue": r.f32(), + "scaleSplashDamage": r.f32(), + "name": r.mstring(), + "internalDamageZone": r.i32(), + "armorZone": r.i32(), + "damageLevel": r.i32(), + "armorType": r.i32(), + "maxArmorValue": r.f32(), + "attachedToZone": r.i32(), + } + out.append(("armor", rec)) + elif class_id == INTERNAL_CLASS: + rec = { + "baseInternalDamage": r.f32(), + "currentInternalDamage": r.f32(), + "parentEntityName": r.mstring(), + "damageMode": r.i32(), + "damageZone": r.i32(), + "damagePropagationZone": r.i32(), + "internalType": r.i32(), + "attachedTo": r.i32(), + } + count = r.i32() + rec["effects"] = [(r.u32(), r.f32()) for _ in range(count)] + rec["missileSlots"] = r.i32() + rec["projectileSlots"] = r.i32() + rec["beamSlots"] = r.i32() + rec["omniSlots"] = r.i32() + out.append(("internal", rec)) + else: + raise ValueError(f"unknown classID {class_id} at offset {r.o - 4}") + return out + + +def fmt(x): + """Match the authored style: 1.0, 80.0, 0.14, .99""" + if x == int(x): + return f"{x:.1f}" + return f"{x:g}" + + +def decompile(mech_dir, manifest=None): + """-> [(pageName, [(key, value)])] in stream order.""" + manifest = manifest if manifest is not None else subsystems.load_manifest(MANIFEST) + blob = record(mech_dir) + pages = [] + for kind, r in parse(blob): + if kind == "armor": + kv = [ + ("BaseArmorValue", fmt(r["baseArmorValue"])), + ("MaxArmorValue", fmt(r["maxArmorValue"])), + ("ScaleSplashDamage", f"{r['scaleSplashDamage']:g}"), + ("InternalDamageZone", INTERNAL_ZONE.get(r["internalDamageZone"])), + ("ArmorZone", ARMOR_ZONE.get(r["armorZone"])), + ] + if r["attachedToZone"] != -1: + kv.append(("SpecialAttachedToZone", ARMOR_ZONE.get(r["attachedToZone"]))) + pages.append((r["name"], kv)) + else: + zone = INTERNAL_ZONE.get(r["damageZone"], "Null") + kv = [ + ("DamageZone", zone), + ("BaseInternalDamage", fmt(r["baseInternalDamage"])), + ] + for rid, pct in r["effects"]: + path = manifest.get(rid >> 16, f"") + kv.append(("DamageEffect", f"{path},{pct:g}")) + # no source writes GeneralDamageMode explicitly, so 0 means "omitted" + if r["damageMode"]: + kv.append(("DamageMode", DAMAGE_MODE.get(r["damageMode"]))) + kv.append(("ParentEntityName", r["parentEntityName"])) + if r["damagePropagationZone"] != -1: + kv.append(("DamagePropagationZone", + INTERNAL_ZONE.get(r["damagePropagationZone"]))) + if r["attachedTo"] != -1: + kv.append(("SpecialAttachedToZone", ARMOR_ZONE.get(r["attachedTo"]))) + for key, field in (("MissileSlots", "missileSlots"), + ("ProjectileSlots", "projectileSlots"), + ("BeamSlots", "beamSlots"), + ("OmniSlots", "omniSlots")): + if r[field]: + kv.append((key, str(r[field]))) + pages.append((zone + "Internal", kv)) + return pages + + +def record(mech_dir): + ch = os.path.basename(mech_dir.rstrip("/")).lower() + for fn in os.listdir(mech_dir): + if fn.lower().endswith(".damage"): + return open(os.path.join(mech_dir, fn), "rb").read() + raise SystemExit(f"no .damage record in {mech_dir}") + + +def emit(pages): + lines = [] + for name, kv in pages: + lines.append(f"[{name}]") + lines.extend(f"{k}={v}" for k, v in kv) + lines.append("") + return "\r\n".join(lines) + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("mech_dir", nargs="?") + ap.add_argument("-o", "--output") + ap.add_argument("-m", "--manifest", default=MANIFEST) + ap.add_argument("--verify", action="store_true") + args = ap.parse_args() + if args.verify: + here = os.path.dirname(os.path.abspath(__file__)) + raise SystemExit(subprocess.call([sys.executable, + os.path.join(here, "verify_damage.py")])) + if not args.mech_dir: + ap.error("mech_dir required") + text = emit(decompile(args.mech_dir, subsystems.load_manifest(args.manifest))) + if args.output: + with open(args.output, "wb") as fh: + fh.write(text.encode("latin-1")) + print(f"wrote {args.output}") + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/data.py b/MW4COMPARE/tools/decompile/data.py new file mode 100644 index 00000000..0106cc32 --- /dev/null +++ b/MW4COMPARE/tools/decompile/data.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python3 +""" +data.py - decompiler for a mech `.data` file. + +Rebuilds the `[GameData]` page from the compiled records: + + .data{GameModel} 1636-byte flat struct (datamap.py) + .data{FootSteps} foot-step texture list + .data[shadow] inline Shadow notation block + +Value sources, in order of preference: + + * a struct member - typed read through datamap.chain_layout() + * a ResourceID member - record id (HIGH word) resolved via the manifest + * a symbolic constant - int reversed through constants.py + * an explicit factory key - handled below, because SaveGameModel writes these + outside the attribute table + +Four keys cannot be recovered and are deliberately omitted: BattleDamageRatio, +BattleKillBonus, DragoonValue and VehicleTradeValue. They appear in every source +.data but are read by NOTHING in the engine - no factory, no attribute +registration, no runtime reference - so they never enter the package. They are +authoring metadata; omitting them changes no behaviour. + + python3 data.py [-o out.data] + python3 data.py --verify +""" +import argparse, collections, os, glob, re, struct, subprocess, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import datamap, constants, subsystems + +MANIFEST = "/home/rich/Repositories/FS_Ours_extracted/_manifest.tsv" + +# Adept.hpp:212. NoMaterial is 0. +MATERIALS = [ + "NoMaterial", "Grass", "Water", "Concrete", "GreyDirt", "BrownDirt", "Rock", + "DarkConcrete", "DarkGreyDirt", "DarkBrownDirt", "DarkRock", "Blacktop", + "Snow", "Wood", "Lava", "Glass", "Steel", "Us", "Them", "LightMineral", + "DarkMineral", "Ash", "CrackedLava", "OpenLava", +] + +# Identical in all 64 chassis; SaveGameModel writes them outside the attribute table. +CONSTANTS = { + "Class": "MechWarrior4::Mech", + "FaceLighting": "yes", + "LookupLighting": "yes", + "VertexLighting": "yes", + "LightMapLighting": "no", + # byte-identical in all 89 mech .data files, destroyed variants included + "Shadow": "{\n[shadow]\nLightType=Shadow\nInnerRadius=4.0\nOuterRadius=10.0\n" + "BlobDistance=200.0\nShadowMap=ShadowMask\nIntensity=0.4\n}", +} + +# Keys whose source name differs from the struct member name. +ALIASES = { + "AnimationScript": "animScriptName", + "HeatManager": "heatManagerResource", + "FootEffectsFile": "footFallEffectsTable", +} + +# CraterName is stored as MString::GetHashValue (DeathEntity_Tool.cpp:34), which +# is one-way. All 64 chassis hold the same hash, so the single authored value is +# recoverable by constancy rather than by inversion. +CRATER_HASH = {217688981: "crater01"} + +# Read by nothing in the engine - see module docstring. +UNRECOVERABLE = ["BattleDamageRatio", "BattleKillBonus", "DragoonValue", "VehicleTradeValue"] + +SYMBOLIC = ("MechID", "TechType", "NameIndex", "MoveTypeFlag") + + +def stem(mech_dir): + """File stem used inside a mech folder; it need not match the folder name. + + jenner2c/ holds jenner_2c.*, the same trap Black Hawk/nova sprang on + .subsystems -- never assume the folder name. + """ + names = [f for f in os.listdir(mech_dir) if f.lower().endswith(".data")] + if names: + return names[0][:-len(".data")] + for f in os.listdir(mech_dir): + m = re.match(r'(.+)\.data[\{\[]', f, re.I) + if m: + return m.group(1) + return os.path.basename(mech_dir.rstrip("/")) + + +def record(mech_dir, suffix): + """Read one qualified record. Not glob -- '[shadow]' is a character class.""" + want = (stem(mech_dir) + suffix).lower() + for fn in os.listdir(mech_dir): + if fn.lower() == want: + return open(os.path.join(mech_dir, fn), "rb").read() + return None + + +def bool_words(): + """-> {key: (trueWord, falseWord)}; sources spell these inconsistently. + + Collider and CanBeShot are written true/false, the CanLoad* flags Yes/No. + """ + seen = collections.defaultdict(collections.Counter) + for _ch, kv, _b in datamap.corpus(): + for k, v in kv.items(): + w = v.strip().lower() + if w in ("true", "false", "yes", "no"): + seen[k][w] += 1 + out = {} + for k, c in seen.items(): + plain = c["true"] + c["false"] >= c["yes"] + c["no"] + out[k] = ("true", "false") if plain else ("Yes", "No") + return out + + +def foot_steps(blob): + """-> (defaultTexture, [(texture, materialName)]). + + Stream written by Mech_Tool.cpp:196: int material (-1 = default), int length + NOT counting the terminator, the characters, a NUL, then a one-byte + isDefault flag. + """ + default, rows, off = None, [], 0 + while off + 8 <= len(blob): + material, length = struct.unpack_from(" OrderedDict key -> value or [values].""" + manifest = manifest if manifest is not None else subsystems.load_manifest(MANIFEST) + ch = os.path.basename(mech_dir.rstrip("/")) + gm = record(mech_dir, ".data{GameModel}") + if gm is None: + raise SystemExit(f"no GameModel record in {mech_dir}") + + layout = datamap.chain_layout()[0] + angles = datamap.angle_fields() + tables = constants.tables() + words = bool_words() + by_member = {datamap.norm(n): n for n in layout} + out = collections.OrderedDict() + + def member(key): + n = ALIASES.get(key) or by_member.get(datamap.norm(key)) + return (n, *layout[n]) if n in layout else None + + # every key the corpus knows about, so output matches the authored shape. + # Keys only a handful of mechs author (VehicleBattleValue: 1 of 64) are left + # out rather than emitted at their default. + corpus = datamap.corpus() + common = collections.Counter(k for _c, kv, _b in corpus for k in kv) + for key in sorted(common): + if key in UNRECOVERABLE or common[key] * 2 < len(corpus): + continue + if key in CONSTANTS: + out[key] = CONSTANTS[key] + continue + if key == "CraterName": + name = CRATER_HASH.get(datamap.read(gm, *layout["m_craterID"])) + if name: + out[key] = name + continue + m = member(key) + if key in SYMBOLIC and m: + _n, off, typ, size = m + raw = datamap.read(gm, off, typ, size) + sym = constants.symbol(tables, key, raw, hint=ch) + # MechID/NameIndex are authored as $(M_Chassis)/$(IDS_Chassis). A + # foreign package may store an int from a different roster -- V4H's + # are shifted by one against ours -- so --retarget-ids emits the + # chassis's own symbol and lets the build resolve it. + if retarget_ids and key in ("MechID", "NameIndex"): + prefix = "M_" if key == "MechID" else "IDS_" + if not sym or constants.norm(ch) not in constants.norm(sym): + out[key] = f"$({prefix}{ch[:1].upper() + ch[1:]})" + continue + if sym: + out[key] = sym + continue + if m: + name, off, typ, size = m + val = datamap.read(gm, off, typ, size, datamap.norm(name) in angles + or typ == "Radian") + if typ == "ResourceID": + path = manifest.get(val >> 16) + if path: + out[key] = path + elif typ in datamap.VECTORS: + out[key] = " ".join(f"{v:g}" for v in val) + elif typ in ("bool", "BYTE"): + yes, no = words.get(key, ("true", "false")) + out[key] = yes if val else no + elif typ in datamap.FLOATS: + out[key] = f"{val:g}" + elif typ == "char": + if val: + out[key] = val + else: + out[key] = str(val) + + # The OBB filenames are source-side names the package never stores. Prefer the + # .obb files actually shipped next to the output so the keys cannot disagree + # with them; fall back to the usual convention when none are present. + solid = hier = None + if obb_dir and os.path.isdir(obb_dir): + for fn in sorted(os.listdir(obb_dir)): + if not fn.lower().endswith(".obb"): + continue + if fn.lower().endswith("_solid.obb"): + solid = fn + else: + hier = fn + out["SolidOBB"] = solid or f"{ch}_Skeleton_SOLID.obb" + out["HierarchicalOBB"] = hier or f"{ch}_Skeleton.obb" + + fs = record(mech_dir, ".data{FootSteps}") + if fs: + default, rows = foot_steps(fs) + if default: + out["DefaultFootStepTexture"] = default + if rows: + out["FootStepTexture"] = [f"{t},{m}" for t, m in rows] + + sh = record(mech_dir, ".data[shadow]") + if sh is None: + out.pop("Shadow", None) + return out + + +def emit(kv): + lines = ["[GameData]"] + for key, val in kv.items(): + for v in (val if isinstance(val, list) else [val]): + lines.append(f"{key}={v}") + return "\r\n".join(lines) + "\r\n" + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("mech_dir", nargs="?") + ap.add_argument("-o", "--output") + ap.add_argument("-m", "--manifest", default=MANIFEST, + help="package manifest for resolving ResourceIDs") + ap.add_argument("--retarget-ids", action="store_true", + help="emit the chassis's own MechID/NameIndex symbol when the " + "stored int belongs to a different roster") + ap.add_argument("--verify", action="store_true") + args = ap.parse_args() + if args.verify: + here = os.path.dirname(os.path.abspath(__file__)) + raise SystemExit(subprocess.call([sys.executable, + os.path.join(here, "verify_roundtrip.py")])) + if not args.mech_dir: + ap.error("mech_dir required") + text = emit(decompile(args.mech_dir, subsystems.load_manifest(args.manifest), + retarget_ids=args.retarget_ids, + obb_dir=os.path.dirname(args.output) if args.output else None)) + if args.output: + with open(args.output, "wb") as fh: + fh.write(text.encode("latin-1")) + print(f"wrote {args.output}") + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/datamap.py b/MW4COMPARE/tools/decompile/datamap.py new file mode 100644 index 00000000..1b6f4dda --- /dev/null +++ b/MW4COMPARE/tools/decompile/datamap.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python3 +""" +datamap.py - field map for the mech `.data{GameModel}` record (1636 B). + +Built from the engine headers, not from value matching. + +Why: value matching alone cannot separate keys that hold the same value in every +mech. `dampenWorldJoint` and `fallAdjustmentSeconds` are both 0.5 everywhere, so +34 of the 80 numeric keys came out ambiguous, and ordering them by their +appearance in the source file assigned several of them wrongly - it put +`tiltSpeed` at 672, which is really `slopeDecel2`, and swapped +`percentageOfTurnToStartTilt` with `percentageOfSpeedToStartTilt`. + +So the layout is taken from the declaration order in + + Vehicle__GameModel mw4/Code/MW4/Vehicle.hpp + Mech__GameModel mw4/Code/MW4/Mech.hpp + +and each block is anchored using the fields that value matching *did* resolve +unambiguously. Every anchor agrees: + + Mech block base 756: footReturnSeconds 764, dampenTorsoJoint 800, + undampenRootJoint 816, undampenHipJoint 824, + scaleInternalTiltDegree 832 + Vehicle block base 664: minSpeed 692, maxSpeed 696, acceleration 720, + decceleration 724, reverseAccelerationMultiplier 728 + +All fields in both blocks are 4-byte Scalar/Radian, so field i sits at +base + 4*i. +""" +import glob, math, os, re, struct, collections + +REPO = "/home/rich/Repositories/firestorm/Gameleap" +SRC = REPO + "/mw4/Content/Mechs" +CODE = REPO + "/code" +REC = "/home/rich/Repositories/FS_Ours_extracted/core/mechs" +GAMEMODEL_SIZE = 1636 + +# The GameModel inheritance chain for a mech, base class first. Bases are NOT +# hardcoded: each block starts where the previous one ended. MWMover__GameModel +# is just `typedef Adept::Mover__GameModel` (MWMover.hpp:173), so it adds +# nothing. `Entity__GameModel` declares no base class and wraps its members in +# `#if NSWIZZLE`, which is never defined anywhere in the tree - the #else branch +# is live, and the two branches order their members DIFFERENTLY. +CHAIN = [ + ("Entity__GameModel", CODE + "/mw4/Libraries/Adept/Entity.hpp"), + ("Mover__GameModel", CODE + "/mw4/Libraries/Adept/Mover.hpp"), + ("MWObject__GameModel", CODE + "/mw4/Code/MW4/MWObject.hpp"), + ("Vehicle__GameModel", CODE + "/mw4/Code/MW4/Vehicle.hpp"), + ("Mech__GameModel", CODE + "/mw4/Code/MW4/Mech.hpp"), +] +# Independently measured block starts, used to check the computed chain. +ANCHORS = {"Vehicle__GameModel": 664, "Mech__GameModel": 756} +# The factories that convert authored degrees into stored radians. +FACTORIES = [CODE + "/mw4/Code/MW4/Mech_Tool.cpp", CODE + "/mw4/Code/MW4/Vehicle_Tool.cpp"] +SCALAR_TYPES = r'(?:Stuff::Scalar|Stuff::Radian|Stuff::Angle|float)' +DEG2RAD = math.pi / 180.0 +MAX_STRING_LENGTH = 256 # Entity.hpp:199 + +# Member sizes. bool is ONE byte, not four -- getting this wrong shifts every +# field after the six m_canLoad* flags by exactly 16 bytes. +SIZES = { + "Scalar": 4, "Radian": 4, "Angle": 4, "float": 4, "int": 4, "unsigned": 4, + "DWORD": 4, "WORD": 2, "BYTE": 1, "bool": 1, "ResourceID": 4, + "Point3D": 12, "Vector3D": 12, "UnitQuaternion": 16, "RGBAColor": 16, + "Motion3D": 24, "LinearMatrix4D": 48, "char": 1, + "ClassID": 4, "ReplicatorID": 4, "FactoryRequest": 4, "ObjectID": 4, +} +MEMBER_RE = re.compile( + r'\b((?:Stuff::|Adept::)?(?:Scalar|Radian|Angle|Point3D|Vector3D|UnitQuaternion' + r'|RGBAColor|Motion3D|LinearMatrix4D|ResourceID)|(?:Stuff::)?(?:RegisteredClass::)?ClassID' + r'|ReplicatorID|ObjectID|(?:\w+::)?FactoryRequest|float|int|bool|BYTE|WORD|DWORD' + r'|unsigned|char)\s+([A-Za-z_][\w\s,\[\]]*?);', re.S) + + +def members(cls, path): + """-> [(typeName, memberName, sizeInBytes)] in declaration order.""" + txt = open(path, encoding="latin-1").read() + m = re.search(rf'class\s+{cls}\s*(?::[^{{;]*)?\{{', txt, re.S) + if not m: + return [] + depth, i = 1, m.end() # brace-match; indentation is inconsistent between headers + while i < len(txt) and depth: + depth += (txt[i] == "{") - (txt[i] == "}") + i += 1 + body = txt[m.end():i - 1] + ctor = body.find(f"{cls}(") + if ctor > 0: + body = body[:ctor] + body = re.sub(r'//[^\n]*', '', body) + # `typedef int AttributeID;` is not a member. This masked a missing ClassID + # for a while: two 4-byte errors that happened to cancel. + body = re.sub(r'\btypedef\b[^;]*;', '', body, flags=re.S) + body = re.sub(r'#\s*if\s+NSWIZZLE\b.*?#\s*else', '', body, flags=re.S) + body = re.sub(r'#\s*(endif|else|if\w*|ifdef|ifndef)[^\n]*', '', body) + out = [] + for d in MEMBER_RE.finditer(body): + base = (d.group(1).replace("Stuff::", "").replace("Adept::", "") + .replace("RegisteredClass::", "")) + base = base.rsplit("::", 1)[-1] + for name in d.group(2).split(","): + name = name.strip() + arr = re.fullmatch(r'([A-Za-z_]\w*)\s*\[\s*([A-Za-z_]\w*|\d+)\s*\]', name) + if arr: + n = arr.group(2) + count = MAX_STRING_LENGTH if n == "MaxStringLength" else int(n) + out.append((base, arr.group(1), SIZES[base] * count)) + elif re.fullmatch(r'[A-Za-z_]\w*', name): + out.append((base, name, SIZES[base])) + return out + + +def chain_layout(chain=None, anchors=None, start=0): + """-> ({memberName: (offset, typeName, size)}, {className: baseOffset}). + + Each block starts where the previous ended; /Zp4 means align = min(4, size). + Raises if a computed base contradicts an independently measured anchor. + Defaults to the mech chain; pass another for Torso/Engine and friends. + """ + chain = CHAIN if chain is None else chain + anchors = ANCHORS if anchors is None else anchors + fields, bases, off = {}, {}, start + for cls, path in chain: + off += (-off) % 4 + bases[cls] = off + want = anchors.get(cls) + if want is not None and off != want: + raise AssertionError(f"{cls} computed at {off}, measured {want}") + for typ, name, size in members(cls, path): + off += (-off) % min(4, size) + fields.setdefault(name, (off, typ, size)) + off += size + return fields, bases + + +def angle_fields(): + """Fields the tool factory scales by Radians_Per_Degree. + + This cannot be inferred from the header: torsoHitSpringMotionLimit and its + siblings are declared plain Stuff::Scalar, yet Mech_Tool.cpp:889 stores + `model->torsoHitSpringMotionLimit * Radians_Per_Degree`. Only the writer + knows. Stuff::Radian fields (tiltSpeed, tiltDegree, topSpeedTurnRate, + fullStopTurnRate) are handled by their declared type as well. + """ + out = set() + for path in FACTORIES: + if not os.path.exists(path): + continue + txt = open(path, encoding="latin-1").read() + for m in re.finditer(r'model->(\w+)\s*=\s*model->\w+\s*\*\s*Radians_Per_Degree', txt): + out.add(norm(m.group(1))) + return out + + +def declared_fields(cls, path): + """-> [(fieldName, isAngle)] in declaration order, scalars only. + + isAngle marks Stuff::Radian / Stuff::Angle. Those are authored in DEGREES in + the .data but stored in RADIANS in the record, so they need a pi/180 factor + on the way in and 180/pi on the way back out. + """ + txt = open(path, encoding="latin-1").read() + m = re.search(rf'class\s+{cls}\s*:(.*?)^\t\t\}};', txt, re.S | re.M) + if not m: + return [] + body = m.group(1) + ctor = body.find(f"{cls}(") + if ctor > 0: + body = body[:ctor] + body = re.sub(r'//[^\n]*', '', body) + out = [] + for decl in re.finditer(rf'\b({SCALAR_TYPES})\s+([A-Za-z_][\w\s,]*?);', body, re.S): + angle = decl.group(1) in ("Stuff::Radian", "Stuff::Angle") + for name in decl.group(2).split(","): + n = name.strip() + if re.fullmatch(r'[A-Za-z_]\w*', n): + out.append((n, angle)) + return out + + +def norm(name): + return re.sub(r'^m_', '', name).replace("_", "").lower() + + +def header_field_map(): + """normalised member name -> (offset, typeName, size, isAngle).""" + angles = angle_fields() + out = {} + for name, (off, typ, size) in chain_layout()[0].items(): + key = norm(name) + out.setdefault(key, (off, typ, size, typ == "Radian" or key in angles)) + return out + + +def gamedata(path): + txt = open(path, "rb").read().decode("latin-1") + # Shadow={...} contains a line reading "[shadow]"; without hiding braced + # blocks the page scan stops there and every later key is lost silently. + txt = re.sub(r'\{.*?\}', + lambda x: x.group(0).replace("\r", "").replace("\n", "\x01"), + txt, flags=re.S) + m = re.search(r'^\[GameData\]\r?\n(.*?)(?=^\[[A-Za-z]|\Z)', txt, re.M | re.S) + if not m: + return collections.OrderedDict() + kv = collections.OrderedDict() + for k, v in re.findall(r'^([A-Za-z0-9_]+)=([^\r\n]*)', m.group(1), re.M): + kv.setdefault(k, v.replace("\x01", "\n")) + return kv + + +def corpus(): + """-> [(chassis, {key: value}, gameModelBytes)] for our 64 mech chassis.""" + out = [] + for d in sorted(glob.glob(REC + "/*")): + ch = os.path.basename(d) + gm = [g for g in glob.glob(d + "/*.data{GameModel}") + if os.path.basename(g).lower().startswith(ch.lower() + ".data")] + if not gm: + continue + blob = open(gm[0], "rb").read() + if len(blob) != GAMEMODEL_SIZE: + continue + s = [p for p in glob.glob(SRC + "/*/*.data") + if os.path.basename(p).lower() == ch.lower() + ".data"] + if s: + out.append((ch, gamedata(s[0]), blob)) + return out + + +def build(): + """-> (corpus, {sourceKey: (offset, typeName, size, isAngle)})""" + pairs = corpus() + hmap = header_field_map() + keys = collections.Counter() + for _c, kv, _b in pairs: + keys.update(kv.keys()) + return pairs, {k: hmap[norm(k)] for k in keys if norm(k) in hmap} + + +FLOATS = {"Scalar", "Radian", "Angle", "float"} +VECTORS = {"Point3D": 3, "Vector3D": 3, "RGBAColor": 4, "UnitQuaternion": 4} + + +def read(blob, off, typ="Scalar", size=4, angle=False): + """Decoded value in the units and form the .data source uses.""" + if typ in FLOATS: + v = struct.unpack_from(" [--erf X.erf] [--obb X.obb] [-o outDir] + python3 destroyed.py --verify # regenerate all 89 of ours and diff + +A destroyed variant is four files: + + .data text, DeathEntity boilerplate + .video text, four-page render graph + .erf geometry, verbatim in the package + _SOLID.obb collision, verbatim in the package + +The .erf and .obb come straight out of the package. The .data and .video are +compiled away (a 12-byte stub plus {Element}/{GameModel}, and a binary element +tree with embedded #FRE/#RLM blobs), so they are regenerated from a template. + +That is safe here because the template is invariant. Across all 89 destroyed +variants in our own Content tree, every one of Class, OBBCollides, Collider, +CanBeShot, CanBeWalkedOn, VertexLighting, FaceLighting, LookupLighting, +LightMapLighting and CraterName is identical. The only things that vary are the +three filename references, and those are read from the files actually present +rather than guessed - which also preserves V4H's own `champion_stroyed` typo, +since that is the name their build really uses. + +The .data and .video each appear in two forms in our tree, differing only by a +trailing blank line; the majority form is emitted. +""" +import sys, os, re, glob, argparse, collections + +DATA_TEMPLATE = ( + "[GameData]\r\n" + "Class=MechWarrior4::DeathEntity\r\n" + "SolidOBB={obb}\r\n" + "OBBCollides=false\r\n" + "Collider=false\r\n" + "CanBeShot=false\r\n" + "CanBeWalkedOn=false\r\n" + "VertexLighting=yes\r\n" + "FaceLighting=yes\r\n" + "LookupLighting=no\r\n" + "LightMapLighting=no\r\n" + "CraterName=crater1\r\n" + "\r\n" + "[Renderers]\r\n" + "VideoRenderer={video}\r\n" + "\r\n" +) + +VIDEO_TEMPLATE = ( + "[lod]\r\n" + "Type=ShapeComponent\r\n" + "Geometry={erf}\r\n" + "\r\n" + "[watcher]\r\n" + "Type=AttributeWatcherOfInt\r\n" + "Attribute=VisualRepresentation\r\n" + "SimulationShouldExecute=1\r\n" + "\r\n" + "[damageappearance]\r\n" + "Type=SwitchComponent\r\n" + "Input=Watcher\r\n" + "Child=LOD\r\n" + "\r\n" + "[locator]\r\n" + "Child=DamageAppearance\r\n" + "\r\n" +) + +OUR_MECH_SOURCE = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Mechs" + + +def find_parts(mech_dir, compiled_dir=None): + """-> (stem, erfName, obbName). Looks in mech_dir, then the compiled tree.""" + erfs = [f for f in os.listdir(mech_dir) if f.lower().endswith(".erf")] + obbs = [f for f in os.listdir(mech_dir) if f.lower().endswith(".obb")] + stem = None + if compiled_dir and os.path.isdir(compiled_dir): + datas = [f for f in os.listdir(compiled_dir) + if f.lower().endswith(".data")] + if datas: + stem = datas[0][:-len(".data")] + if stem is None and obbs: + stem = re.sub(r"_solid\.obb$", "", obbs[0], flags=re.I) + stem = re.sub(r"\.obb$", "", stem, flags=re.I) + return stem, (erfs[0] if erfs else None), (obbs[0] if obbs else None) + + +def generate(stem, erf, obb): + return (DATA_TEMPLATE.format(obb=obb, video=f"{stem}.video"), + VIDEO_TEMPLATE.format(erf=erf)) + + +def verify(): + ok = collections.Counter() + problems = [] + for src_dir in sorted(glob.glob(OUR_MECH_SOURCE + "/*_[Dd]estroyed") + + glob.glob(OUR_MECH_SOURCE + "/*_DESTROYED")): + datas = glob.glob(src_dir + "/*.data") + videos = glob.glob(src_dir + "/*.video") + if not datas or not videos: + continue + stem = os.path.basename(datas[0])[:-len(".data")] + want_data = open(datas[0], "rb").read().decode("latin-1") + want_video = open(videos[0], "rb").read().decode("latin-1") + kv = dict(re.findall(r'^([A-Za-z0-9_]+)=([^\r\n]*)', want_data, re.M)) + erf = re.search(r'Geometry=([^\r\n]*)', want_video).group(1).strip() + got_data, got_video = generate(stem, erf, kv["SolidOBB"].strip()) + + ok["files"] += 1 + for what, want, got, exact_key, soft_key in ( + (".data", want_data, got_data, "data_exact", "data_soft"), + (".video", want_video, got_video, "video_exact", "video_soft")): + if got == want: + ok[exact_key] += 1 + elif got.rstrip("\r\n").lower() == want.rstrip("\r\n").lower(): + # NotationFile compares with _stricmp throughout, and the sources + # themselves are inconsistent (one uses [renderers], others + # [Renderers]), so case and a trailing blank line are cosmetic. + ok[soft_key] += 1 + else: + problems.append((os.path.basename(src_dir), what, want, got)) + + print(f"destroyed variants checked : {ok['files']}") + print(f" .data equivalent : {ok['data_exact'] + ok['data_soft']}" + f" (byte-exact {ok['data_exact']}, +{ok['data_soft']} case / trailing-blank-line only)") + print(f" .video equivalent : {ok['video_exact'] + ok['video_soft']}" + f" (byte-exact {ok['video_exact']}, +{ok['video_soft']} case / trailing-blank-line only)") + if problems: + print(f"\n{len(problems)} real differences:") + for name, what, want, got in problems[:6]: + print(f" {name} {what}") + for w, g in zip(want.splitlines(), got.splitlines()): + if w != g: + print(f" source={w!r}\n got ={g!r}") + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("mechdir", nargs="?", help="the *_destroyed folder holding .erf/.obb") + ap.add_argument("--compiled", help="matching folder in the _compiled tree, for the stem") + ap.add_argument("-o", "--out", help="output folder (defaults to mechdir)") + ap.add_argument("--verify", action="store_true") + args = ap.parse_args() + + if args.verify: + verify() + return + if not args.mechdir: + ap.error("give a *_destroyed folder, or --verify") + + stem, erf, obb = find_parts(args.mechdir, args.compiled) + if not (stem and erf and obb): + sys.exit(f"{args.mechdir}: need a stem, an .erf and an .obb " + f"(got stem={stem!r} erf={erf!r} obb={obb!r})") + data, video = generate(stem, erf, obb) + out = args.out or args.mechdir + os.makedirs(out, exist_ok=True) + open(os.path.join(out, f"{stem}.data"), "wb").write(data.encode("latin-1")) + open(os.path.join(out, f"{stem}.video"), "wb").write(video.encode("latin-1")) + print(f"{os.path.basename(args.mechdir)}: {stem}.data + {stem}.video " + f"(SolidOBB={obb}, Geometry={erf})") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/instance.py b/MW4COMPARE/tools/decompile/instance.py new file mode 100644 index 00000000..bab2146e --- /dev/null +++ b/MW4COMPARE/tools/decompile/instance.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +""" +instance.py - decompiler for a mech `.instance` file. + +One page named after the chassis, holding the model/armature/subsystem/damage +references and the mechlab bar ratings. The record is a single +`Mech__CreateMessage`, so the layout comes from the CreateMessage chain rather +than the GameModel one: + + Replicator -> Entity -> Mover -> MWMover -> MWObject -> Vehicle -> Mech + +`datamap.chain_layout(..., start=16)` computes it -- 16 because the +`Connection__Message` header (messageLength, priority, flags) sits in front and +is not declared in any of these classes. The result ends at 341 and pads to +exactly the 344-byte record, and every offset established independently while +decoding `.armature` lands on the nose: classID 16, replicatorID 24, +localToParent 28, dataListID 84, alignment 88, jointName 152. + +Two parser gaps had to be closed to get there, both fields typed with names the +member regex did not know: `Stuff::RegisteredClass::ClassID` / `ReplicatorID` in +the Replicator base, and `Entity__ExecutionStateEngine::FactoryRequest` / +`ObjectID` in Entity. Missing them silently shifted everything after offset 76 +by 8 bytes while still producing a plausible-looking table. + + python3 instance.py [-o out.instance] + python3 instance.py --verify +""" +import argparse, os, re, struct, subprocess, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import datamap, mw4msg, subsystems + +MANIFEST = "/home/rich/Repositories/FS_Ours_extracted/_manifest.tsv" +C = datamap.CODE +CHAIN = [ + ("Replicator__CreateMessage", C + "/mw4/Libraries/Adept/Replicator.hpp"), + ("Entity__CreateMessage", C + "/mw4/Libraries/Adept/Entity.hpp"), + ("Mover__CreateMessage", C + "/mw4/Libraries/Adept/Mover.hpp"), + ("MWMover__CreateMessage", C + "/mw4/Code/MW4/MWMover.hpp"), + ("MWObject__CreateMessage", C + "/mw4/Code/MW4/MWObject.hpp"), + ("Vehicle__CreateMessage", C + "/mw4/Code/MW4/Vehicle.hpp"), + ("Mech__CreateMessage", C + "/mw4/Code/MW4/Mech.hpp"), +] +CONNECTION_HEADER = 16 + +EXEC_STATE = {1: "NeverExecuteState", 2: "AlwaysExecuteState", 6: "ActiveState"} +# Entity.hpp:894 +ALIGNMENT = {0: "DefaultAlignment", 1: "Player", 2: "Enemy", + 3: "Team1", 4: "Team2", 5: "Team3", 6: "Team4"} + +# source key -> message member, in the order 61 of 89 sources use +KEYS = [ + ("Model", "dataListID"), + ("ExecutionState", "executionState"), + ("Armature", "armatureStreamResourceID"), + ("Subsystems", "subsystemStreamResourceID"), + ("DamageObjects", "damageStreamResourceID"), + ("Alignment", "alignment"), + ("CurrentHeat", "currentHeat"), + ("CurrentCoolant", "currentCoolant"), + ("MaxCoolant", "maxCoolant"), + ("DoesHaveInstanceName", "doesHaveInstanceName"), + ("PowerRating", "m_powerBar"), + ("ArmorRating", "m_armorBar"), + ("SpeedRating", "m_speedBar"), + ("HeatRating", "m_heatBar"), +] + + +def record(mech_dir): + for fn in os.listdir(mech_dir): + if fn.lower().endswith(".instance"): + return open(os.path.join(mech_dir, fn), "rb").read() + raise SystemExit(f"no .instance record in {mech_dir}") + + +def relative(path, chassis): + """References are written relative to the mech folder.""" + if not path: + return path + return re.sub(rf'^mechs[\\/]{re.escape(chassis or "")}[\\/]', '', path, flags=re.I) + + +def num(x): + return f"{int(x)}" if float(x) == int(x) else f"{x:g}" + + +def decompile(mech_dir, manifest=None): + """-> (pageName, [(key, value)])""" + manifest = manifest if manifest is not None else subsystems.load_manifest(MANIFEST) + blob = record(mech_dir) + layout, _ = datamap.chain_layout(CHAIN, {}, start=CONNECTION_HEADER) + + msgs = list(mw4msg.walk(blob, start=0)) + if len(msgs) != 1: + raise SystemExit(f"expected one message in {mech_dir}, got {len(msgs)}") + msg = msgs[0][1] + + name = datamap.read(msg, *layout["jointName"]) + folder = os.path.basename(mech_dir.rstrip("/")) + kv = [] + for key, member in KEYS: + off, typ, size = layout[member] + val = datamap.read(msg, off, typ, size) + if member == "executionState": + kv.append((key, EXEC_STATE.get(val, str(val)))) + elif member == "alignment": + kv.append((key, ALIGNMENT.get(val, str(val)))) + elif typ == "ResourceID": + path = manifest.get(val >> 16) + kv.append((key, relative(path, folder) if path else "")) + elif typ == "bool": + kv.append((key, "yes" if val else "no")) + elif typ in datamap.FLOATS: + kv.append((key, num(val))) + else: + kv.append((key, str(val))) + return name, kv + + +def emit(name, kv): + lines = [f"[{name}]"] + [f"{k}={v}" for k, v in kv] + return "\r\n".join(lines) + "\r\n" + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("mech_dir", nargs="?") + ap.add_argument("-o", "--output") + ap.add_argument("-m", "--manifest", default=MANIFEST) + ap.add_argument("--verify", action="store_true") + args = ap.parse_args() + if args.verify: + here = os.path.dirname(os.path.abspath(__file__)) + raise SystemExit(subprocess.call([sys.executable, + os.path.join(here, "verify_instance.py")])) + if not args.mech_dir: + ap.error("mech_dir required") + name, kv = decompile(args.mech_dir, subsystems.load_manifest(args.manifest)) + text = emit(name, kv) + if args.output: + with open(args.output, "wb") as fh: + fh.write(text.encode("latin-1")) + print(f"wrote {args.output}") + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/make_generic_doll.py b/MW4COMPARE/tools/decompile/make_generic_doll.py new file mode 100644 index 00000000..bf51b22c --- /dev/null +++ b/MW4COMPARE/tools/decompile/make_generic_doll.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 +""" +make_generic_doll.py - build a generic MFD/Radar damage paper doll. + +The six chassis imported from V4H have no damage art. V4H shipped Mad Cat copies +under six names, which renders an aligned doll that lies about the mech. Instead +this builds one honest generic doll from art the game already owns. + +Source: `hsh/mfd_texture.bmp`, the MFD sprite sheet, which contains a generic +mech already separated into components, together with a validated 11-zone +mapping in `huddamage.cpp` (`sm2_texture` / `sm2_offset`, lines 472 and 487) -- +the same zone order `coord.cpp` uses. `RenderAux4SmallMech` draws it, so the +geometry below is proven in game rather than measured by us. + +Output is the exploded runtime BMP: every zone appears exactly once, and no two +source rectangles touch, so each `texuv` isolates one component. The game +reassembles the figure from the `offset` values. + + python3 make_generic_doll.py [-o OUTDIR] +""" +import argparse, os, sys + +from PIL import Image, ImageDraw + +HSH = "/home/rich/Repositories/firestorm/Gameleap/mw4/hsh" + +# huddamage.cpp:472 -- source rects into mfd_texture.bmp, in coord.cpp zone order +SM2_TEXTURE = [ + (172, 0, 231, 140, "LL", "left leg"), + (109, 0, 168, 140, "RL", "right leg"), + (30, 144, 67, 246, "LA", "left arm"), + (0, 36, 36, 128, "RA", "right arm"), + (37, 24, 66, 101, "RT", "right torso"), + (0, 132, 28, 209, "LT", "left torso"), + (67, 33, 99, 125, "CT", "center torso"), + (None, None, None, None, "CTR", "center torso rear"), + (67, 0, 99, 31, "HD", "head"), + (None, None, None, None, "S1", "special 1"), + (None, None, None, None, "S2", "special 2"), +] +# huddamage.cpp:487 -- assembled positions (the *3 is already applied here) +SM2_OFFSET = [ + (29 * 3, 35 * 3), (6 * 3, 35 * 3), (43 * 3, 11 * 3), (0 * 3, 11 * 3), + (12 * 3, 7 * 3), (33 * 3, 7 * 3), (22 * 3, 10 * 3), None, + (22 * 3, 0 * 3), None, None, +] + +# The native figure is 166x245. Real dolls assemble to roughly 330x335 (Atlas, +# Assassin II), so 1.5 lands in the right range and keeps the arithmetic exact: +# the generic centre torso becomes 48x138 against Atlas's 48x174. +# +# Radar is authored on a 410 working canvas against the MFD's 340, and real rows +# follow that -- Assassin II's radar rects run about 1.27x its MFD ones -- so the +# Radar doll is scaled up to match. The runtime halves Radar values at draw time. +MFD_SCALE = 1.5 +RADAR_SCALE = 1.8 +CANVAS = 512 + + +def even(v): + """Coordinates are authored even; runtime Radar halves them with integer /2.""" + return int(round(v / 2.0)) * 2 + + +def components(scale): + sheet = Image.open(os.path.join(HSH, "mfd_texture.bmp")).convert("L") + out = [] + for (rect, off) in zip(SM2_TEXTURE, SM2_OFFSET): + x0, y0, x1, y1, zone, label = rect + if x0 is None: + out.append((zone, label, None, None, None)) + continue + piece = sheet.crop((x0, y0, x1, y1)) + w = even((x1 - x0) * scale) + h = even((y1 - y0) * scale) + piece = piece.resize((w, h), Image.LANCZOS) + out.append((zone, label, piece, (even(off[0] * scale), even(off[1] * scale)), (w, h))) + return out + + +def layout(comps, outline=False): + """Place every piece in the 512 canvas without touching. -> (image, {zone: rect})""" + img = Image.new("L", (CANVAS, CANVAS), 0) + draw = ImageDraw.Draw(img) + rects = {} + x, y, row_h, gap = 4, 4, 0, 8 + for zone, _label, piece, _off, size in comps: + if piece is None: + rects[zone] = (0, 0, 0, 0) + continue + w, h = size + if x + w + gap > CANVAS: + x = 4 + y += row_h + gap + row_h = 0 + img.paste(piece, (x, y)) + if outline: + draw.rectangle([x, y, x + w - 1, y + h - 1], outline=255, width=2) + rects[zone] = (x, y, x + w, y + h) + x += w + gap + row_h = max(row_h, h) + return img, rects + + +def row(values, width=3): + return "{" + ",".join("{" + ",".join(f"{v:{width}d}" for v in t) + "}" for t in values) + "}" + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("-o", "--outdir", default="/home/rich/Repositories/FS_Build_V4H_extracted") + args = ap.parse_args() + + comps = components(MFD_SCALE) + order = [c[0] for c in comps] + + written = [] + tables = {} + for kind, sub, outlined, scale in (("MFD", "hsh/hud", False, MFD_SCALE), + ("Radar", "hsh/radar/hud", True, RADAR_SCALE)): + comps = components(scale) + img, rects = layout(comps, outline=outlined) + d = os.path.join(args.outdir, sub) + os.makedirs(d, exist_ok=True) + p = os.path.join(d, "generic.bmp") + img.save(p) + written.append(p) + texuv = [rects[z] for z in order] + offset = [(c[3] if c[3] else (0, 0)) for c in comps] + offset = [o if rects[z] != (0, 0, 0, 0) else (0, 0) for z, o in zip(order, offset)] + tables[kind] = (texuv, offset) + + doc = os.path.join(args.outdir, "hsh", "GENERIC-DOLL-COORDS.txt") + with open(doc, "w", newline="\r\n") as fh: + fh.write(TEXT.format( + scale=MFD_SCALE, + rscale=RADAR_SCALE, + zones=" ".join(f"{i}:{z}" for i, z in enumerate(order)), + mfd_texuv=row(tables["MFD"][0]), + mfd_offset=row(tables["MFD"][1]), + rad_texuv=row(tables["Radar"][0]), + rad_offset=row(tables["Radar"][1]), + )) + written.append(doc) + for p in written: + print("wrote", p) + + +TEXT = """GENERIC MECH DAMAGE PAPER DOLL - coordinates for coord.cpp +========================================================= + +WHAT THIS IS + A generic 'Mech damage doll for chassis that have no bespoke MFD/Radar art. + Six imported V4H chassis (champion, dasher, griffin, jenner2c, marauder, + thunderbolt) ship with none. V4H filled the gap with pixel-identical copies + of the Mad Cat doll under six names, which renders an aligned display that + misrepresents the mech. This is the honest placeholder instead. + +WHERE THE ART CAME FROM + Not drawn from scratch. The game already owns a generic 'Mech, separated + into components, inside hsh/mfd_texture.bmp (the MFD sprite sheet), with a + validated eleven-zone mapping in huddamage.cpp: + + sm2_texture[][4] line 472 source rectangles + sm2_offset[][2] line 487 assembled positions + + drawn by RenderAux4SmallMech(). Those tables use the same zone order as + coord.cpp, so the geometry below is proven in game, not measured by us. + Components were scaled {scale}x for the MFD and {rscale}x for the Radar. The + native figure is 166x245 and real dolls assemble to roughly 330x335, so this + lands in the right range; the generic centre torso ends up 48x138 against the + Atlas's 48x174. The Radar is larger because it is authored on a 410 working + canvas against the MFD's 340, and the runtime halves Radar values at draw. + +FILES + hsh/hud/generic.bmp 512x512 external MFD doll + hsh/radar/hud/generic.bmp 512x512 Radar doll, components outlined 2px + white per the Radar pipeline + + Both are the EXPLODED runtime view: each zone appears exactly once and no + two source rectangles touch, so a texuv isolates one component. The game + reassembles the figure from the offsets. + +ZONE ORDER (index: zone) + {zones} + + CTR, S1 and S2 are all-zero, and that is normal rather than a shortcut. Across + the 65 shipped rows CTR is zero in ALL 65, S1 in 31 and S2 in 49. Neither + draw loop guards zero rectangles - a degenerate quad simply renders nothing - + so absent zones are safe. The generic figure has no rear silhouette and no + special hardpoints. + +ONE FILE SERVES ALL SIX + The BMP name comes only from `texturename[]` in huddamage.cpp; both displays + use it (`LoadDamageTexture` and `LoadRadarDamageTexture` are both called with + `texturename[m_MechID]`). Nothing in the mech data files names it. So six + entries pointing at "hud\\\\generic" share one pair of images - do not make + per-chassis copies. (`m_HudMap` is unrelated: that is the mission map.) + +-------------------------------------------------------------------------- +HOW TO INSTALL +-------------------------------------------------------------------------- + +1. Copy the art + hsh/hud/generic.bmp -> Gameleap/mw4/hsh/hud/generic.bmp + hsh/radar/hud/generic.bmp -> Gameleap/mw4/hsh/radar/hud/generic.bmp + Loose hsh art is not packed into a .mw4, so no resource rebuild is needed. + +2. huddamage.cpp - add one texturename[] entry per new chassis, all pointing at + the same generic art. The array is sized [LastMechID+1], so LastMechID in + MechLabHeaders.h must also rise from 64 to 70. + + "hud\\\\generic", // M_Champion 65 + "hud\\\\generic", // M_Jenner2c 66 + "hud\\\\generic", // M_Dasher 67 + "hud\\\\generic", // M_Marauder 68 + "hud\\\\generic", // M_Thunderbolt 69 + "hud\\\\generic", // M_Griffin 70 + + Keep V4H's append order (65-70). Inserting alphabetically would renumber + every existing chassis, and Mech IDs are positional across code, tables and + shell scripts. + +3. coord.cpp - widen all four arrays from [65] to [71] and append the SAME row + six times to each, once per new chassis. All six share one doll, so all six + rows are identical. + + texuv2 / offset2 are the external MFD. texuv3 / offset3 are the Radar, which + the runtime divides by two at draw time - store the full-size values here, + do not pre-divide. + +texuv2 (MFD source rectangles) +{mfd_texuv} + +offset2 (MFD exploded positions) +{mfd_offset} + +texuv3 (Radar source rectangles) +{rad_texuv} + +offset3 (Radar exploded positions) +{rad_offset} + +4. Rebuild. DXRasterizer.cpp includes coord.cpp directly, so a coordinate change + needs a Release/Profile rebuild and redeploy of MW4.exe. The art alone does + not. + +-------------------------------------------------------------------------- +NOTES AND LIMITS +-------------------------------------------------------------------------- + + * Dasher's four commented-out rows were REMOVED from coord.cpp on 2026-08-08. + They were unique authored Dasher geometry rather than a copy, but the art they + mapped has never existed, and leaving them invited someone to enable them + against wrong art. Recoverable from git (last touched in deafc2b0) if real + Dasher art is ever produced. The matching commented `"hud\\\\dasher"` entry at + huddamage.cpp:81 is still there and should go with them. + + * The generic cannot represent chassis-specific zones. Marauder's cage and + Dasher's S1/S2 will not appear. + + * This is a placeholder. The point of a generic silhouette over a borrowed one + is that it reads as "no art yet" rather than as the wrong mech. Replace it + per chassis when real 1024x1024 renders exist; the authoring pipelines are + in MFD-RADAR-MAPPINGS.md. + + * PRE-EXISTING BUGS, unrelated to this work but found while checking it. Three + chassis share another mech's rows while having their own art. Overlaying each + row on its own art shows: + Rifleman (id 49, uses Mad Cat's rows) - BROKEN. CT lands in empty + space beside the mech, S1/S2 float in empty corners, and the + leg boxes run well past the feet. + Battlemaster (id 9, uses Atlas's rows) - BROKEN. CT sits over a + left-side arm piece and the leg boxes are narrow centre + strips while the real legs fall outside them. + Templar (id 54, uses Archer's rows) - fine. Its art was evidently + authored to that layout; only the leg boxes clip slightly. + Rifleman and Battlemaster need their own measured rows, or art authored to + the borrowed layout. +""" + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/mw4msg.py b/MW4COMPARE/tools/decompile/mw4msg.py new file mode 100644 index 00000000..0ec7e889 --- /dev/null +++ b/MW4COMPARE/tools/decompile/mw4msg.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +""" +mw4msg.py - reader for the GameOS "CreateMessage" streams found inside .mw4 +records (.subsystems, [joint_*]{armature}, .contents, .instance, ...). + +Layout, derived from the engine source rather than guessed: + + MWObject::CreateSubsystemStream / CreateArmatureStream (MWObject_Tool.cpp) + WORD span number of replicator IDs consumed + N x CreateMessage one per [Page] that was serialised + + Each message is a plain C struct, /Zp4, built by a per-class factory in one of + the 51 mw4/Code/MW4/*_Tool.cpp files. The common prefix is: + + off 0 u32 messageLength Connection__Message + off 4 u32 messageID + off 8 u32 priority + off 12 u32 messageFlags + off 16 u32 classID Replicator__CreateMessage + off 20 u32 replicatorFlags + off 24 u32 replicatorID + off 28 12f localToParent Entity__CreateMessage (LinearMatrix4D) + off 76 u32 executionState + off 80 f32 initialAge + off 84 u32 dataListID (ResourceID; record id is the HIGH word) + off 88 u32 alignment + off 92 u32 nameID + + Mover__CreateMessage then adds two 24-byte Motion3D fields (96, 120), and + MWMover__CreateMessage adds: + + off 144 u32 siteStreamResourceID + off 148 u32 armatureStreamResourceID + off 152 char jointName[128] + + Verified: a joint message is exactly 280 bytes, which is 152 + 128. + +localToParent is 3 rows of 4 floats; the rotation is columns 0..2 and the +translation is column 3 of each row. Checked against every joint of all 65 +chassis: 1453/1453 translations matched the source .armature exactly. +""" +import struct + +HDR_LEN = 0 +HDR_CLASSID = 16 +HDR_REPLICATORID = 24 +ENT_MATRIX = 28 +ENT_EXECSTATE = 76 +ENT_INITIALAGE = 80 +ENT_DATALISTID = 84 +ENT_ALIGNMENT = 88 +ENT_NAMEID = 92 +MWMOVER_SITEID = 144 +MWMOVER_ARMID = 148 +MWMOVER_JOINTNAME = 152 + + +def walk(data, start=2): + """Yield (offset, messageBytes) for each message in a span-prefixed stream.""" + off = start + while off + 4 <= len(data): + length, = struct.unpack_from(" len(data): + raise ValueError(f"bad messageLength {length} at offset {off}") + yield off, data[off:off + length] + off += length + if off != len(data): + raise ValueError(f"trailing {len(data) - off} bytes") + + +def span(data): + return struct.unpack_from("> 16 + + +def matrix(msg): + return struct.unpack_from("<12f", msg, ENT_MATRIX) + + +def translation(msg): + m = matrix(msg) + return (m[3], m[7], m[11]) + + +def rotation3x3(msg): + m = matrix(msg) + return (m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]) + + +def joint_name(msg): + if len(msg) <= MWMOVER_JOINTNAME: + return None + return msg[MWMOVER_JOINTNAME:].split(b"\0")[0].decode("latin-1") + + +def read_sites(data): + """{sites} record: repeated [YawPitchRoll 3f][Point3D 3f][u32 len][name][NUL]. + + Written by MWMover__CreateMessage::ConstructCreateMessage (MWMover_Tool.cpp + ~145): `site_stream << rotation; << translation; << site_name;` + Angles are radians. + """ + out, off = [], 0 + while off + 28 <= len(data): + rx, ry, rz, tx, ty, tz = struct.unpack_from("<6f", data, off) + off += 24 + n, = struct.unpack_from(" Subsystem__GameModel -> Torso__GameModel 1608 bytes + Entity__GameModel -> Subsystem__GameModel -> Engine__GameModel 64 bytes + +Both chains compute to exactly the record size with no anchoring, which is the +check that the member list and alignment are right. + +Sources author the numbers as `$(SYMBOL)` macros from a `!include`d defines file +(`!NAME=value` syntax, not `#define`), and the record keeps only the resolved +float, so the symbol is restored by reverse lookup where one matches. + +Two quirks worth keeping: + + * The five Torso angles are declared plain `Stuff::Scalar` but `Torso_Tool.cpp` + multiplies them by `Radians_Per_Degree`, exactly like the Mech spring fields. + * `TotalCritLocations` is read by **nothing** in the engine -- only the 3DS Max + export plugin writes it, and the factory reads `TotalSlotsTaken`, which no + source sets (the record holds the default 1 while every source says 2). It + is emitted as the constant it always is rather than derived. + + python3 smallmodel.py torso|engine [-o out] + python3 smallmodel.py --verify +""" +import argparse, collections, os, re, subprocess, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import datamap + +C = datamap.CODE +CONTENT = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content" + +SPECS = { + "torso": { + "chain": [("Entity__GameModel", C + "/mw4/Libraries/Adept/Entity.hpp"), + ("Subsystem__GameModel", C + "/mw4/Code/MW4/Subsystem.hpp"), + ("Torso__GameModel", C + "/mw4/Code/MW4/Torso.hpp")], + "size": 1608, + "factory": C + "/mw4/Code/MW4/Torso_Tool.cpp", + "defines": CONTENT + "/Defines/MechTorso.defines", + "include": r"Content\Defines\MechTorso.defines", + "class": "MechWarrior4::Torso", + "keys": [("TwistJointName", "twistJointName"), + ("PitchJointName", "pitchJointName"), + ("LeftArmJointName", "leftArmJointName"), + ("RightArmJointName", "rightArmJointName"), + ("EyeJointName", "eyeJointName"), + ("ArmRatioAngle", "armRatioAngle"), + ("TwistSpeed", "twistSpeed"), + ("PitchSpeed", "pitchSpeed"), + ("TwistRadius", "twistRadius"), + ("PitchRadius", "pitchRadius"), + ("CageJointName", "cageJointName"), + ("CageRatioAngle", "cageRatioAngle")], + }, + "engine": { + "chain": [("Entity__GameModel", C + "/mw4/Libraries/Adept/Entity.hpp"), + ("Subsystem__GameModel", C + "/mw4/Code/MW4/Subsystem.hpp"), + ("Engine__GameModel", C + "/mw4/Code/MW4/Engine.hpp")], + "size": 64, + "factory": C + "/mw4/Code/MW4/Engine_Tool.cpp", + "defines": CONTENT + "/Subsystems/HeatSink.Defines", + "include": r"Content\Subsystems\HeatSink.defines", + "class": "Mechwarrior4::Engine", # lowercase 'w' in every source + "keys": [("NumHeatSinks", "m_numHeatSinks"), + ("TonsPerUpgrade", "m_tonsPerUpgrade"), + ("MPSPerUpgrade", "m_mpsPerUpgrade"), + ("HeatSinkEfficiency", "m_heatSinkEfficiency")], + }, +} + +# Written only by the 3DS Max exporter; the engine never reads it. Uniformly 2. +TOTAL_CRIT_LOCATIONS = "2" + + +def defines(path): + """-> {value: symbol} from a `!NAME=value` defines file.""" + out = {} + if not os.path.exists(path): + return out + txt = open(path, encoding="latin-1", errors="replace").read() + txt = re.sub(r'//[^\n]*', '', txt) + for name, val in re.findall(r'^\s*!(\w+)\s*=\s*([-\d.]+)\s*$', txt, re.M): + out.setdefault(round(float(val), 6), name) + return out + + +def angle_members(factory): + out = set() + if not os.path.exists(factory): + return out + txt = open(factory, encoding="latin-1").read() + for m in re.finditer(r'model->(\w+)\s*=\s*model->\w+\s*\*\s*Radians_Per_Degree', txt): + out.add(m.group(1)) + return out + + +def record(mech_dir, kind): + for fn in os.listdir(mech_dir): + if fn.lower().endswith(f".{kind}{{gamemodel}}"): + return open(os.path.join(mech_dir, fn), "rb").read() + return None + + +def num(x): + return f"{int(x)}" if float(x) == int(x) else f"{x:g}" + + +def decompile(mech_dir, kind): + """-> [(key, value)] for the [GameData] page.""" + spec = SPECS[kind] + blob = record(mech_dir, kind) + if blob is None: + raise SystemExit(f"no .{kind}{{GameModel}} record in {mech_dir}") + layout, _ = datamap.chain_layout(spec["chain"], {}) + angles = angle_members(spec["factory"]) + symbols = defines(spec["defines"]) + + kv = [("Class", spec["class"]), ("TotalCritLocations", TOTAL_CRIT_LOCATIONS)] + for key, member in spec["keys"]: + off, typ, size = layout[member] + val = datamap.read(blob, off, typ, size, member in angles) + if typ == "char": + kv.append((key, val)) + elif typ == "int": + kv.append((key, str(val))) + else: + sym = symbols.get(round(val, 6)) + kv.append((key, f"$({sym})" if sym else num(val))) + return kv + + +def emit(kind, kv): + lines = [f"!include = {SPECS[kind]['include']}", "", "[GameData]"] + lines += [f"{k}={v}" for k, v in kv] + return "\r\n".join(lines) + "\r\n" + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("kind", nargs="?", choices=sorted(SPECS)) + ap.add_argument("mech_dir", nargs="?") + ap.add_argument("-o", "--output") + ap.add_argument("--verify", action="store_true") + args = ap.parse_args() + if args.verify: + here = os.path.dirname(os.path.abspath(__file__)) + raise SystemExit(subprocess.call([sys.executable, + os.path.join(here, "verify_smallmodel.py")])) + if not args.kind or not args.mech_dir: + ap.error("kind and mech_dir required") + text = emit(args.kind, decompile(args.mech_dir, args.kind)) + if args.output: + with open(args.output, "wb") as fh: + fh.write(text.encode("latin-1")) + print(f"wrote {args.output}") + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/subsystems.py b/MW4COMPARE/tools/decompile/subsystems.py new file mode 100644 index 00000000..6128b1ff --- /dev/null +++ b/MW4COMPARE/tools/decompile/subsystems.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +""" +subsystems.py - rebuild a mech's .subsystems source from its packed record. + + python3 subsystems.py [-o out.subsystems] + python3 subsystems.py --verify # check against all known chassis + +The packed record is `WORD span` followed by one CreateMessage per [Page] +(MWObject::CreateSubsystemStream, MWObject_Tool.cpp:1196). Message layout and +the derivation of every offset below is documented in ../../DECOMPILING.md. + +Layout beyond the Entity header (Subsystem.hpp, Weapon.hpp, Armor.hpp): + + off 96 i32 subsystemIndex + off 100 u8 locationID -> InternalLocation + off 104 i32 criticalHitsTaken -> CriticalHitsTaken + Armor (152): + off 108 9xf32 armour points -> tons, see ARMOR_POINTS_PER_TON + off 144 i32 m_armorType -> ArmorType + off 148 i32 m_internalType -> InternalType + Engine (112): + off 108 i32 m_engineUpgrades -> EngineUpgrades + SearchLight (236): + off 108 char[128] siteName -> Site + Weapon (380, or 376 without the trailing field): + off 108 char[128] siteName -> Site + off 236 char[128] ejectSiteName -> EjectSite + off 364 i32 groupIndex -> GroupIndex + off 368 i32 ammoCount -> AmmoCount + off 372 i32 initialAmmoCount + off 376 i32 m_weaponFacing -> WeaponFacing (absent when len == 376) + +`m_weaponFacing` is the "MSL 5.04 Rear Firing Weapons" field. V4H packed +champion/griffin/marauder without it (376 bytes) - matching their own release +note "Any new mech will not have rear facing weapons" - while dasher, jenner2c +and thunderbolt have it. Both forms are read; re-packing with our own exe always +emits the 380-byte form. + +PAGE NAMES ARE NOT STORED. The packer only serialises page order, so names are +regenerated from the Model= reference with a per-kind counter. They are labels; +the runtime keys on order. +""" +import sys, os, re, glob, struct, argparse, collections + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import mw4msg + +OUR_MECH_SOURCE = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Mechs" +OUR_RECORDS = "/home/rich/Repositories/FS_Ours_extracted/core/mechs" +OUR_MANIFEST = "/home/rich/Repositories/FS_Ours_extracted/_manifest.tsv" +ARMOR_DATA = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Subsystems/Armor.data" + +# Derived empirically from 63 aligned chassis and cross-checked against the +# engine's own text tables. +EXEC_STATE = {1: "NeverExecuteState", 2: "AlwaysExecuteState", 6: "ActiveState"} +# InternalDamageObject enum, DamageObject.hpp:205 +ZONE = {255: "NullZone", 0: "LeftLeg", 1: "RightLeg", 2: "LeftArm", 3: "RightArm", + 4: "RightTorso", 5: "LeftTorso", 6: "CenterTorso", 7: "Head", + 8: "Special1", 9: "Special2", 10: "VehicleHull", 11: "VehicleWeapon", + 12: "VehicleSpecial", 13: "DefaultZone"} +ARMOR_TYPE = {0: "Standard", 1: "FerroFiberus", 2: "Reactive", 3: "Reflective", + 4: "Solarian"} +# Armor.hpp:275 - a separate 2-value enum, not the armour table. (The engine's own +# InternalTypeAsciiToText returns the typo "Statndard"; TextToAscii wants "Standard".) +INTERNAL_TYPE = {0: "Standard", 1: "EndoSteel"} +ARMOR_KEYS = ["LeftLeg", "RightLeg", "LeftArm", "RightArm", "LeftFrontTorso", + "RightFrontTorso", "CenterFrontTorso", "CenterRearTorso", "Head"] + +CLASS_ENGINE, CLASS_LAMS = 1073, 1183 + +# Model= basename -> page-name stem. Anything unmatched falls back to the +# weapon-category table below. +PAGE_STEM = { + "heatsinksubsystem.data": "HeatSink", "armor.data": "Armor", + "advancedgyrosubsystem.data": "AdvancedGyro", "sensorsubsystem.data": "Sensor", + "searchlightsubsystem.data": "SearchLight", "jumpjetsubsystem.data": "JumpJet", + "ecmsubsystem.data": "ECM", "beaglesubsystem.data": "Beagle", + "lams.data": "LAMS", "narcbeacon.data": "Narc", +} +WEAPON_CATEGORY = [ + ("laserweaponsubsystem", "Beam"), ("pulselaserweaponsubsystem", "Beam"), + ("ppcweaponsubsystem", "Beam"), ("flamerweaponsubsystem", "Beam"), + ("lrmweaponsubsystem", "Missile"), ("srmweaponsubsystem", "Missile"), + ("ssrmweaponsubsystem", "Missile"), ("smrmweaponsubsystem", "Missile"), + ("missileweaponsubsystem", "Missile"), ("narcbeaconweaponsubsystem", "Narc"), + ("machinegunweaponsubsystem", "Ballistic"), ("ultraacweaponsubsystem", "Ballistic"), + ("acweaponsubsystem", "Ballistic"), ("gaussweaponsubsystem", "Ballistic"), + ("lbxweaponsubsystem", "Ballistic"), ("rtxweaponsubsystem", "Ballistic"), +] + + +def armor_points_per_ton(path=ARMOR_DATA): + txt = open(path, "rb").read().decode("latin-1") + + def get(key, default): + m = re.search(rf"^{key}=(\d+)", txt, re.M | re.I) + return int(m.group(1)) if m else default + return {0: get("PointsPerStandardTon", 32), 1: get("PointsPerFerroTon", 38), + 2: get("PointsPerReactiveTon", 30), 3: get("PointsPerReflectiveTon", 30), + 4: get("PointsPerSolarianTon", 60)} + + +def load_manifest(path, package="core.mw4"): + """record id -> entry name, for resolving dataListID back to a Model= path.""" + out = {} + with open(path, encoding="latin-1") as fh: + for line in fh: + parts = line.rstrip("\n").split("\t") + if len(parts) > 2 and parts[0].lower() == package.lower(): + out[int(parts[1])] = parts[2] + return out + + +def cstr(msg, off, size=128): + return msg[off:off + size].split(b"\0")[0].decode("latin-1").strip() + + +def group_flags_to_list(flags): + """groupIndex is a BITMASK, not an index (Weapon_Tool.cpp ~76). + + A page may carry several `GroupIndex=` lines; the factory ORs + `1 << (n-1)` for each. Returns the group numbers, so the caller can emit one + line per group. + """ + return [n for n in range(1, 7) if flags & (1 << (n - 1))] + + +def page_name(model, counters): + base = os.path.basename(model.replace("\\", "/")).lower() + stem = PAGE_STEM.get(base) + if stem is None: + low = model.lower().replace("\\", "/") + stem = next((s for key, s in WEAPON_CATEGORY if key in low), None) + if stem is None: + stem = "Torso" if base.endswith(".torso") else \ + "Engine" if base.endswith(".engine") else "Subsystem" + counters[stem] += 1 + # Singletons keep a bare name, matching how the sources are written. + if stem in ("Armor", "AdvancedGyro", "Sensor", "SearchLight", "Torso", + "Engine", "ECM", "Beagle", "LAMS"): + return stem if counters[stem] == 1 else f"{stem}{counters[stem]}" + return f"{stem}{counters[stem]}" + + +def decompile(record_path, manifest, ppt=None): + ppt = ppt or armor_points_per_ton() + data = open(record_path, "rb").read() + # Model= is written relative to the mech's own directory in the source, but the + # package stores the full entry path. Use the parent folder, not the file name: + # Black Hawk's chassis files are nova.* inside mechs/blackhawk/. + own_dir = "mechs\\" + os.path.basename(os.path.dirname(record_path)).lower() + "\\" + counters = collections.Counter() + pages = [] + for _off, msg in mw4msg.walk(data): + cid, n = mw4msg.class_id(msg), len(msg) + model = manifest.get(mw4msg.record_id(msg), "?") + if model.lower().startswith(own_dir): + model = model[len(own_dir):] + kv = collections.OrderedDict() + kv["Model"] = model + kv["ExecutionState"] = EXEC_STATE.get(struct.unpack_from("= 0: + kv["AmmoCount"] = str(ammo) + elif n == 236: # SearchLight + kv["Site"] = cstr(msg, 108) + elif n in (376, 380): # Weapon + kv["Site"] = cstr(msg, 108) + eject = cstr(msg, 236) + grp, ammo, _init = struct.unpack_from("<3i", msg, 364) + kv["GroupIndex"] = group_flags_to_list(grp) + if ammo >= 0: + kv["AmmoCount"] = str(ammo) + if eject: + kv["EjectSite"] = eject + if n == 380: + facing = struct.unpack_from(" {args.out}") + else: + sys.stdout.write(text) + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/verify_armature.py b/MW4COMPARE/tools/decompile/verify_armature.py new file mode 100644 index 00000000..63f44a53 --- /dev/null +++ b/MW4COMPARE/tools/decompile/verify_armature.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Verification harness for the .armature decompiler. + +Rebuilds every chassis from our own packed records and compares against the +known source .armature. Compares page transforms as a multiset keyed on +(name, transform) so duplicate page names - Victor has two [site_lshellport] +pages under different joints - are handled. + + python3 verify_armature.py +""" +import sys, os, glob, re, collections + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import armature + + +def source_entries(path): + """-> (list of (name, rot, trans), {parent: [child, ...]})""" + txt = open(path, "rb").read().decode("latin-1") + entries, children = [], {} + for m in re.finditer(r'^\[([^\]]+)\]\s*\r?\n(.*?)(?=^\[|\Z)', txt, re.M | re.S): + name, body = m.group(1).lower(), m.group(2) + r = re.search(r'Rotation=([-\d.eE ]+)', body) + t = re.search(r'Translation=([-\d.eE ]+)', body) + kids = [c.lower() for c in re.findall(r'Child=(\S+)', body)] + if kids: + children[name] = kids + if r and t: + entries.append((name, + tuple(float(x) for x in r.group(1).split()), + tuple(float(x) for x in t.group(1).split()))) + return entries, children + + +def close(a, b, tol): + return a and b and max(abs(x - y) for x, y in zip(a, b)) <= tol + + +def angles_close(a, b, tol=0.01): + return a and b and max(abs(((x - y + 180) % 360) - 180) for x, y in zip(a, b)) <= tol + + +def main(): + src_paths = {os.path.basename(p)[:-len(".armature")].lower(): p + for p in glob.glob(armature.OUR_MECH_SOURCE + "/*/*.armature")} + t = collections.Counter() + residual = collections.Counter() + for mech_dir in sorted(glob.glob(armature.OUR_RECORDS + "/*")): + if not glob.glob(mech_dir + "/*{armature}"): + continue + chassis, entries, children = armature.rebuild(mech_dir) + if not chassis or chassis.lower() not in src_paths: + continue + t["chassis"] += 1 + src_list, src_children = source_entries(src_paths[chassis.lower()]) + + got = [(e["name"].lower(), e["rot"], e["trans"]) for e in entries] + pool = list(got) + for name, rot, tr in src_list: + t["pages"] += 1 + hit = next((g for g in pool if g[0] == name + and close(g[2], tr, 2e-3) and angles_close(g[1], rot)), None) + if hit: + pool.remove(hit) + t["exact"] += 1 + continue + near = next((g for g in pool if g[0] == name and close(g[2], tr, 2e-3)), None) + if near: + pool.remove(near) + t["rot_only"] += 1 + residual[name] += 1 + else: + t["missing"] += 1 + residual["MISSING " + name] += 1 + + for parent, kids in src_children.items(): + t["childlists"] += 1 + if sorted(kids) == sorted(c.lower() for c in children.get(parent, ())): + t["childlists_ok"] += 1 + else: + residual["CHILDREN " + parent] += 1 + + print(f"chassis : {t['chassis']}") + print(f"pages compared : {t['pages']}") + print(f" fully exact : {t['exact']}") + print(f" translation ok, rotation lost by the packer : {t['rot_only']}") + print(f" not recovered : {t['missing']}") + print(f"child lists : {t['childlists_ok']}/{t['childlists']} exact") + if residual: + print("\nresidual, by page:") + for k, v in residual.most_common(10): + print(f" {k:22s} x{v}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/verify_contents.py b/MW4COMPARE/tools/decompile/verify_contents.py new file mode 100644 index 00000000..e96c5239 --- /dev/null +++ b/MW4COMPARE/tools/decompile/verify_contents.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Round-trip verifier for the .contents decompiler. + +Regenerates each `.contents` and compares page names and both key values against +the authored source. Page order is not compared -- NotationFile is +order-independent, and the packer groups pages by parent joint rather than +preserving the authored sequence. + + python3 verify_contents.py [--show CHASSIS] +""" +import argparse, collections, glob, os, re, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import contents, subsystems + +REC = "/home/rich/Repositories/FS_Ours_extracted/core/mechs" +SRC = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Mechs" + + +def source_pages(path): + txt = open(path, "rb").read().decode("latin-1") + txt = re.sub(r'//[^\n]*', '', txt) + out = {} + for m in re.finditer(r'^\[([^\]]+)\]\r?\n(.*?)(?=^\[|\Z)', txt, re.M | re.S): + name, body = m.group(1), m.group(2) + if name.lower() == "includes": + continue + kv = dict(re.findall(r'^([A-Za-z_]\w*)=([^\r\n]*)', body, re.M)) + out[name.lower()] = {k.lower(): v.strip().lower().replace("/", "\\") + for k, v in kv.items()} + return out + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--show") + args = ap.parse_args() + + manifest = subsystems.load_manifest(contents.MANIFEST) + t = collections.Counter() + bad = collections.Counter() + examples = [] + missing_pages = collections.Counter() + extra_pages = collections.Counter() + + for d in sorted(glob.glob(REC + "/*")): + ch = os.path.basename(d) + src = [p for p in glob.glob(SRC + "/*/*.contents") + if os.path.basename(p).lower() == ch.lower() + ".contents"] + if not src: + continue + t["chassis"] += 1 + want = source_pages(src[0]) + chassis, pages = contents.decompile(d, manifest) + got = {n.lower(): {k.lower(): (v or "").lower().replace("/", "\\") + for k, v in kv} for n, kv in pages} + + if args.show and args.show.lower() == ch.lower(): + sys.stdout.write(contents.emit(chassis, pages)) + return + + for name in want: + if name not in got: + missing_pages[name] += 1 + for name in got: + if name not in want: + extra_pages[name] += 1 + for name in set(want) & set(got): + t["pages"] += 1 + for key in ("model", "executionstate"): + t["keys"] += 1 + if want[name].get(key) == got[name].get(key): + t["ok"] += 1 + else: + bad[key] += 1 + if len(examples) < 10: + examples.append((ch, name, key, + want[name].get(key), got[name].get(key))) + + print(f"chassis : {t['chassis']}") + print(f"pages compared : {t['pages']}") + print(f"keys compared : {t['keys']} exact: {t['ok']} wrong: {t['keys'] - t['ok']}") + if missing_pages: + print(f"\npages in source but not decoded: {sum(missing_pages.values())}") + for n, c in missing_pages.most_common(10): + print(f" {n:28s} x{c}") + if extra_pages: + print(f"\npages decoded but not in source: {sum(extra_pages.values())}") + for n, c in extra_pages.most_common(10): + print(f" {n:28s} x{c}") + if bad: + print("\nvalue mismatches:") + for k, c in bad.most_common(): + print(f" {k:20s} x{c}") + for e in examples: + print(f" {e[0]:14s} {e[1]:24s} {e[2]:16s} want={e[3]!r} got={e[4]!r}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/verify_damage.py b/MW4COMPARE/tools/decompile/verify_damage.py new file mode 100644 index 00000000..10791e26 --- /dev/null +++ b/MW4COMPARE/tools/decompile/verify_damage.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""Round-trip verifier for the .damage decompiler. + +Regenerates a whole `.damage` from the compiled record for every chassis and +compares it page by page against the authored source: page names, page order, +key sets and values. + +Comparison is semantic. Sources carry an `!include` line and comments, spell +numbers freely (`.99` vs `0.99`, `1.0` vs `1`), and Windows path lookup is +case-insensitive, so none of those count as differences. + + python3 verify_damage.py [--show CHASSIS] +""" +import argparse, collections, glob, os, re, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import damage, subsystems + +REC = "/home/rich/Repositories/FS_Ours_extracted/core/mechs" +SRC = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Mechs" +NUM = re.compile(r'^-?(?:\d+\.?\d*|\.\d+)$') + + +def canon(v): + v = str(v).strip() + if NUM.match(v): + return f"{float(v):.4g}" + if "," in v: # DamageEffect: path,percent + path, _, pct = v.rpartition(",") + return canon(path) + "," + (f"{float(pct):.4g}" if NUM.match(pct.strip()) else pct) + v = re.sub(r'^content[\\/]', '', v.replace("/", "\\"), flags=re.I) + return v.lower() + + +def source_pages(path): + """-> [(pageName, [(key, value)])] preserving order and repeats.""" + txt = open(path, "rb").read().decode("latin-1") + txt = re.sub(r'//[^\n]*', '', txt) + pages, cur = [], None + for line in txt.splitlines(): + line = line.strip() + if not line or line.startswith("!"): + continue + m = re.match(r'^\[([^\]]+)\]$', line) + if m: + cur = (m.group(1), []) + pages.append(cur) + elif "=" in line and cur is not None: + k, v = line.split("=", 1) + cur[1].append((k.strip(), v.strip())) + return pages + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--show") + args = ap.parse_args() + + manifest = subsystems.load_manifest(damage.MANIFEST) + t = collections.Counter() + bad = collections.Counter() + examples = [] + order_problems = [] + + for d in sorted(glob.glob(REC + "/*")): + ch = os.path.basename(d) + src = [p for p in glob.glob(SRC + "/*/*.damage") + if os.path.basename(p).lower() == ch.lower() + ".damage"] + if not src or not glob.glob(os.path.join(d, "*.damage")): + continue + t["chassis"] += 1 + want = source_pages(src[0]) + got = damage.decompile(d, manifest) + + if args.show and args.show.lower() == ch.lower(): + sys.stdout.write(damage.emit(got)) + return + + if [n.lower() for n, _ in want] != [n.lower() for n, _ in got]: + order_problems.append((ch, [n for n, _ in want], [n for n, _ in got])) + continue + t["pages"] += len(want) + for (wname, wkv), (_gname, gkv) in zip(want, got): + wmap = collections.defaultdict(list) + for k, v in wkv: + wmap[k.lower()].append(canon(v)) + gmap = collections.defaultdict(list) + for k, v in gkv: + gmap[k.lower()].append(canon(v)) + for key in set(wmap) | set(gmap): + t["keys"] += 1 + w, g = sorted(wmap.get(key, [])), sorted(gmap.get(key, [])) + # The writer defaults MaxArmorValue to BaseArmorValue, so a source + # that omits it and one that states it equal produce identical + # bytes -- 51 pages do state it. The distinction is unrecoverable + # and harmless, so emitting it explicitly counts as a match. + if not w and key == "maxarmorvalue" and g == sorted(gmap.get("basearmorvalue", [])): + t["ok"] += 1 + t["implicit_max"] += 1 + elif w == g: + t["ok"] += 1 + else: + bad[key] += 1 + if len(examples) < 12: + examples.append((ch, wname, key, wmap.get(key), gmap.get(key))) + + print(f"chassis : {t['chassis']}") + print(f"pages compared : {t['pages']}") + print(f"keys compared : {t['keys']} exact: {t['ok']} wrong: {t['keys'] - t['ok']}") + print(f" MaxArmorValue omitted by source, implied by BaseArmorValue: {t['implicit_max']}") + if order_problems: + print(f"\npage name/order mismatches: {len(order_problems)}") + for ch, w, g in order_problems[:3]: + print(f" {ch}\n want {w}\n got {g}") + if bad: + print("\nkeys not matching:") + for k, n in bad.most_common(15): + print(f" {k:28s} x{n}") + print("\nexamples (chassis, page, key, source, decoded):") + for e in examples: + print(f" {e[0]:14s} {e[1]:22s} {e[2]:22s} {e[3]} != {e[4]}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/verify_data.py b/MW4COMPARE/tools/decompile/verify_data.py new file mode 100644 index 00000000..3d1e9a11 --- /dev/null +++ b/MW4COMPARE/tools/decompile/verify_data.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Verification harness for the mech .data{GameModel} field map. + +Reads every mapped value through its header-derived offset and compares against +the source .data, across all 64 chassis we hold both forms for. + +Unlike a value-discovered map this one can genuinely fail: the offsets come from +declaration order, so a mis-parsed header shows up at once as a whole column of +wrong values. Keys that miss consistently by a constant factor are reported with +their decoded/source ratio, which is how unit conversions (deg -> rad, kph -> +m/s) announce themselves. + + python3 verify_data.py +""" +import sys, os, re, collections + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import datamap + + +def close(a, b): + return abs(a - b) <= max(1e-4, abs(b) * 1e-5) + + +def matches(got, want_text, typ): + """Compare a decoded value against the raw source text.""" + if typ in datamap.VECTORS: + nums = [float(x) for x in re.findall(r'-?\d+\.?\d*(?:[eE][-+]?\d+)?', want_text)] + return len(nums) == len(got) and all(close(a, b) for a, b in zip(got, nums)) + if typ == "char": + return got.lower() == want_text.strip().lower() + if typ in ("bool", "BYTE"): + w = want_text.strip().lower() + if w in ("true", "yes", "1"): + return got == 1 + if w in ("false", "no", "0"): + return got == 0 + return None + try: + return close(float(got), float(want_text)) + except ValueError: + return None + + +def main(): + pairs, field_map = datamap.build() + t = collections.Counter() + bad = collections.Counter() + examples = [] + + for ch, kv, blob in pairs: + for key, (off, typ, size, angle) in field_map.items(): + if key not in kv: + continue + got = datamap.read(blob, off, typ, size, angle) + ok = matches(got, kv[key], typ) + if ok is None: + t["skipped"] += 1 + continue + t["values"] += 1 + if ok: + t["ok"] += 1 + else: + bad[key] += 1 + if len(examples) < 12: + examples.append((ch, key, typ, kv[key], got, off)) + + print(f"chassis : {len(pairs)}") + print(f"mapped keys : {len(field_map)}") + print(f"values compared : {t['values']} exact: {t['ok']} wrong: {t['values'] - t['ok']}") + print(f"not comparable : {t['skipped']} (enum/resource text, decoded separately)") + if bad: + print("\nkeys not matching:") + for k, v in bad.most_common(25): + print(f" {k:34s} x{v}") + print("\nexamples (chassis, key, type, source, decoded, offset):") + for ch, key, typ, src, got, off in examples: + print(f" {ch:14s} {key:28s} {typ:10s} src={src!r:24s} got={got!r} @{off}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/verify_instance.py b/MW4COMPARE/tools/decompile/verify_instance.py new file mode 100644 index 00000000..73edb459 --- /dev/null +++ b/MW4COMPARE/tools/decompile/verify_instance.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Round-trip verifier for the .instance decompiler. + +Regenerates each `.instance` and compares the page name and every key against +the authored source. Keys the source carries but the decompiler does not emit +are reported separately, so an unhandled key can never be mistaken for a pass. + + python3 verify_instance.py [--show CHASSIS] +""" +import argparse, collections, glob, os, re, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import instance, subsystems + +REC = "/home/rich/Repositories/FS_Ours_extracted/core/mechs" +SRC = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Mechs" +NUM = re.compile(r'^-?(?:\d+\.?\d*|\.\d+)$') + + +def canon(v): + v = str(v).strip() + if NUM.match(v): + return f"{float(v):.5g}" + return re.sub(r'^content[\\/]', '', v.replace("/", "\\"), flags=re.I).lower() + + +def source_page(path): + txt = re.sub(r'//[^\n]*', '', open(path, "rb").read().decode("latin-1")) + m = re.search(r'^\[([^\]]+)\]', txt, re.M) + kv = [(k, v.strip()) for k, v in re.findall(r'^([A-Za-z]\w*)=([^\r\n]*)', txt, re.M)] + return (m.group(1) if m else None), kv + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--show") + args = ap.parse_args() + + manifest = subsystems.load_manifest(instance.MANIFEST) + t = collections.Counter() + bad = collections.Counter() + unhandled = collections.Counter() + examples = [] + name_bad = [] + + for d in sorted(glob.glob(REC + "/*")): + ch = os.path.basename(d) + src = [p for p in glob.glob(SRC + "/*/*.instance") + if os.path.basename(p).lower() == ch.lower() + ".instance"] + if not src or not glob.glob(os.path.join(d, "*.instance")): + continue + t["chassis"] += 1 + want_name, want = source_page(src[0]) + got_name, got = instance.decompile(d, manifest) + + if args.show and args.show.lower() == ch.lower(): + sys.stdout.write(instance.emit(got_name, got)) + return + + if (want_name or "").lower() != (got_name or "").lower(): + name_bad.append((ch, want_name, got_name)) + gmap = {k.lower(): v for k, v in got} + for key, wv in want: + if key.lower() not in gmap: + unhandled[key] += 1 + continue + t["keys"] += 1 + if canon(wv) == canon(gmap[key.lower()]): + t["ok"] += 1 + else: + bad[key] += 1 + if len(examples) < 12: + examples.append((ch, key, wv, gmap[key.lower()])) + + print(f"chassis : {t['chassis']}") + print(f"page names : {t['chassis'] - len(name_bad)}/{t['chassis']} match") + print(f"keys compared : {t['keys']} exact: {t['ok']} wrong: {t['keys'] - t['ok']}") + if unhandled: + print("\nkeys present in source but NOT emitted:") + for k, n in unhandled.most_common(): + print(f" {k:26s} x{n}") + if name_bad: + print("\npage name mismatches:") + for e in name_bad[:5]: + print(f" {e[0]:14s} want={e[1]!r} got={e[2]!r}") + if bad: + print("\nvalue mismatches:") + for k, n in bad.most_common(15): + print(f" {k:26s} x{n}") + print("\nexamples (chassis, key, source, decoded):") + for e in examples: + print(f" {e[0]:14s} {e[1]:22s} want={e[2]!r:32s} got={e[3]!r}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/verify_roundtrip.py b/MW4COMPARE/tools/decompile/verify_roundtrip.py new file mode 100644 index 00000000..cfbd2b47 --- /dev/null +++ b/MW4COMPARE/tools/decompile/verify_roundtrip.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +"""Round-trip verifier for the mech .data decompiler. + +Regenerates a whole `.data` from the compiled records for every chassis we hold +both forms of, and compares it key by key against the authored source. This is +the standard `.armature` and `.subsystems` were held to; nothing should be +emitted for the new chassis until this passes. + +Comparison is semantic, not byte-exact: NotationFile is order-independent, and +Windows path lookup is case-insensitive (our own tree writes both +`mechs\\atlas_destroyed\\...` and `Mechs\\Atlas_Destroyed\\...` for the same +key), so keys are matched case-insensitively and numbers within tolerance. + + python3 verify_roundtrip.py [--show CHASSIS] +""" +import argparse, collections, os, re, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import datamap, data, subsystems + +REC = "/home/rich/Repositories/FS_Ours_extracted/core/mechs" +NUM = re.compile(r'^-?\d+\.?\d*(?:[eE][-+]?\d+)?$') + + +def canon(v): + """Normalise one value for comparison.""" + v = str(v).strip() + v = re.sub(r'^content[\\/]', '', v.replace("/", "\\"), flags=re.I) + v = re.sub(r'\s+', " ", v).lower() + # normalise every number in place so "20.0 20.0 20.0" == "20 20 20" + return re.sub(r'-?\d+\.\d+(?:[eE][-+]?\d+)?|-?\d+', + lambda m: f"{float(m.group(0)):.4g}", v) + + +def canon_set(val): + """Sorted, de-duplicated: some sources repeat a key with the same value.""" + return sorted({canon(v) for v in (val if isinstance(val, list) else [val])}) + + +def source_kv(path): + """Authored [GameData] page, keeping repeated keys as lists.""" + txt = open(path, "rb").read().decode("latin-1") + # protect braced blocks first: Shadow={...} contains a line reading + # "[shadow]", which otherwise looks like the start of the next page. + # Both CR and LF must go -- splitlines() splits on a bare CR too. + txt = re.sub(r'\{.*?\}', + lambda x: x.group(0).replace("\r", "").replace("\n", "\x01"), + txt, flags=re.S) + m = re.search(r'^\[GameData\]\r?\n(.*?)(?=^\[[A-Za-z]|\Z)', txt, re.M | re.S) + body = m.group(1) if m else "" + kv = collections.OrderedDict() + for line in body.splitlines(): + if "=" not in line or line.lstrip().startswith("//"): + continue + k, v = line.split("=", 1) + kv.setdefault(k.strip(), []).append(v.replace("\x01", "\n").strip()) + return kv + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--show") + args = ap.parse_args() + + manifest = subsystems.load_manifest(data.MANIFEST) + totals = collections.Counter() + wrong = collections.Counter() + missing = collections.Counter() + extra = collections.Counter() + examples = [] + chassis = 0 + + for ch, kv_ignored, _blob in datamap.corpus(): + src_path = [p for p in + __import__("glob").glob(datamap.SRC + "/*/*.data") + if os.path.basename(p).lower() == ch.lower() + ".data"] + if not src_path: + continue + chassis += 1 + want = source_kv(src_path[0]) + got = data.decompile(os.path.join(REC, ch), manifest) + got_l = {k.lower(): v for k, v in got.items()} + + for key, wvals in want.items(): + if key in data.UNRECOVERABLE: + totals["omitted"] += 1 + continue + totals["keys"] += 1 + if key.lower() not in got_l: + missing[key] += 1 + continue + if canon_set(got_l[key.lower()]) == canon_set(wvals): + totals["ok"] += 1 + else: + wrong[key] += 1 + if len(examples) < 12: + examples.append((ch, key, wvals, got_l[key.lower()])) + for key in got: + if key.lower() not in {k.lower() for k in want}: + extra[key] += 1 + + if args.show and args.show.lower() == ch.lower(): + sys.stdout.write(data.emit(got)) + return + + print(f"chassis : {chassis}") + print(f"keys compared : {totals['keys']} exact: {totals['ok']} " + f"wrong: {sum(wrong.values())} missing: {sum(missing.values())}") + print(f"deliberately omitted: {totals['omitted']} (unreadable by the engine)") + if missing: + print("\nmissing from output:") + for k, n in missing.most_common(15): + print(f" {k:30s} x{n}") + if wrong: + print("\nvalue mismatches:") + for k, n in wrong.most_common(15): + print(f" {k:30s} x{n}") + print("\nexamples:") + for ch, k, w, g in examples: + print(f" {ch:12s} {k:26s} want={w!r:44s} got={g!r}") + if extra: + print("\nemitted but not in source:") + for k, n in extra.most_common(10): + print(f" {k:30s} x{n}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/verify_smallmodel.py b/MW4COMPARE/tools/decompile/verify_smallmodel.py new file mode 100644 index 00000000..54283923 --- /dev/null +++ b/MW4COMPARE/tools/decompile/verify_smallmodel.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Round-trip verifier for the .torso and .engine decompilers. + +Regenerates both files for every chassis and compares key order and values +against the authored source. `$(SYMBOL)` and its numeric expansion are treated +as equal, since the record stores only the resolved float. + + python3 verify_smallmodel.py [--show KIND CHASSIS] +""" +import argparse, collections, glob, os, re, sys + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import smallmodel + +REC = "/home/rich/Repositories/FS_Ours_extracted/core/mechs" +SRC = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content/Mechs" +NUM = re.compile(r'^-?(?:\d+\.?\d*|\.\d+)$') + + +def resolve(value, symbols): + """$(NAME) -> its numeric value, so symbol and literal compare equal.""" + m = re.fullmatch(r'\$\((\w+)\)', value.strip()) + if m: + return symbols.get(m.group(1).lower(), value.strip().lower()) + return value.strip().lower() + + +def canon(value, symbols): + v = resolve(value, symbols) + return f"{float(v):.5g}" if NUM.match(str(v)) else str(v) + + +def symbol_values(path): + """-> {symbolNameLower: numericString}""" + out = {} + if os.path.exists(path): + txt = re.sub(r'//[^\n]*', '', open(path, encoding="latin-1", errors="replace").read()) + for name, val in re.findall(r'^\s*!(\w+)\s*=\s*([-\d.]+)\s*$', txt, re.M): + out[name.lower()] = val + return out + + +def source_kv(path): + txt = re.sub(r'//[^\n]*', '', open(path, "rb").read().decode("latin-1")) + return [(m.group(1), m.group(2).strip()) + for m in re.finditer(r'^([A-Za-z]\w*)=([^\r\n]*)', txt, re.M)] + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--show", nargs=2, metavar=("KIND", "CHASSIS")) + args = ap.parse_args() + + t = collections.Counter() + bad = collections.Counter() + examples = [] + order_bad = [] + + for kind, spec in sorted(smallmodel.SPECS.items()): + symbols = symbol_values(spec["defines"]) + for d in sorted(glob.glob(REC + "/*")): + ch = os.path.basename(d) + src = [p for p in glob.glob(f"{SRC}/*/*.{kind}") + if os.path.basename(p).lower() == f"{ch.lower()}.{kind}"] + if not src or smallmodel.record(d, kind) is None: + continue + t[f"{kind} files"] += 1 + want = source_kv(src[0]) + got = smallmodel.decompile(d, kind) + + if args.show and args.show[0] == kind and args.show[1].lower() == ch.lower(): + sys.stdout.write(smallmodel.emit(kind, got)) + return + + if [k.lower() for k, _ in want] != [k.lower() for k, _ in got]: + order_bad.append((kind, ch, [k for k, _ in want], [k for k, _ in got])) + continue + for (wk, wv), (_gk, gv) in zip(want, got): + t["keys"] += 1 + if canon(wv, symbols) == canon(gv, symbols): + t["ok"] += 1 + else: + bad[f"{kind}.{wk}"] += 1 + if len(examples) < 12: + examples.append((kind, ch, wk, wv, gv)) + + for kind in sorted(smallmodel.SPECS): + print(f"{kind:8s} files : {t[kind + ' files']}") + print(f"keys compared : {t['keys']} exact: {t['ok']} wrong: {t['keys'] - t['ok']}") + if order_bad: + print(f"\nkey order mismatches: {len(order_bad)}") + for kind, ch, w, g in order_bad[:3]: + print(f" {kind} {ch}\n want {w}\n got {g}") + if bad: + print("\nvalue mismatches:") + for k, n in bad.most_common(15): + print(f" {k:34s} x{n}") + print("\nexamples (kind, chassis, key, source, decoded):") + for e in examples: + print(f" {e[0]:7s} {e[1]:14s} {e[2]:22s} want={e[3]!r:22s} got={e[4]!r}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/decompile/verify_subsystems.py b/MW4COMPARE/tools/decompile/verify_subsystems.py new file mode 100644 index 00000000..e44126bd --- /dev/null +++ b/MW4COMPARE/tools/decompile/verify_subsystems.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +"""Verification harness for the .subsystems decompiler. + +Rebuilds every chassis from our own packed records and compares the resulting +key/value pairs, page by page, against the known source .subsystems. + +Page names are ignored - the packer does not store them (see subsystems.py), so +only order and content can be verified. + +Chassis whose source has moved on since the package was built are reported +separately rather than counted as failures; core.mw4 has not been repacked since +the initial mirror, so battlemaster and battlemaster2c legitimately differ. + + python3 verify_subsystems.py +""" +import sys, os, glob, re, collections + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import subsystems + + +def source_pages(path): + """Parse a source .subsystems leniently. + + The runtime NotationFile accepts a page header with no closing bracket - + hellspawn had '[HeatSink10' and sunder '[HeatSink16' - so match on a leading + '[' rather than a full bracketed pattern. + + GroupIndex may appear several times in one page, so values are collected as + lists rather than overwritten. + """ + txt = open(path, "rb").read().decode("latin-1") + out = [] + for m in re.finditer(r'^\[([^\r\n\]]*)\]?[ \t]*\r?\n(.*?)(?=^\[|\Z)', txt, re.M | re.S): + kv = collections.OrderedDict() + for k, v in re.findall(r'^([A-Za-z0-9_]+)=([^\r\n]*)', m.group(2), re.M): + if k in kv: + kv[k] = (kv[k] if isinstance(kv[k], list) else [kv[k]]) + [v] + else: + kv[k] = v + out.append((m.group(1), kv)) + return out + + +def norm(key, value): + """Compare as a sorted list so a single value and a one-element list match.""" + if not isinstance(value, list): + value = [value] + return sorted(_norm1(key, v) for v in value) + + +def _norm1(key, value): + v = str(value).strip() + if key in ("Model", "Site", "EjectSite", "InternalLocation", "ExecutionState", + "ArmorType", "InternalType"): + return v.lower().replace("\\", "/") + try: + return f"{float(v):.4g}" + except ValueError: + return v.lower() + + +def main(): + manifest = subsystems.load_manifest(subsystems.OUR_MANIFEST) + ppt = subsystems.armor_points_per_ton() + srcs = {os.path.basename(p)[:-len(".subsystems")].lower(): p + for p in glob.glob(subsystems.OUR_MECH_SOURCE + "/*/*.subsystems")} + + t = collections.Counter() + mismatched_keys = collections.Counter() + stale, examples = [], [] + + for rec in sorted(glob.glob(subsystems.OUR_RECORDS + "/*/*.subsystems")): + chassis = os.path.basename(rec)[:-len(".subsystems")].lower() + if chassis not in srcs: + continue + src = source_pages(srcs[chassis]) + got = subsystems.decompile(rec, manifest, ppt) + if len(src) != len(got): + stale.append((chassis, len(got), len(src))) + continue + t["chassis"] += 1 + for (_sname, skv), (_gname, gkv) in zip(src, got): + t["pages"] += 1 + page_ok = True + for key, sval in skv.items(): + if key in ("SubsystemIndex",): # not emitted; defaults to 0 + continue + # An explicit "=0" and an omitted key are identical to the packer. + if gkv.get(key) is None and str(sval).strip() in ("0", "0.0"): + continue + t["keys"] += 1 + gval = gkv.get(key) + if gval is not None and norm(key, gval) == norm(key, sval): + t["keys_ok"] += 1 + else: + page_ok = False + mismatched_keys[key] += 1 + if len(examples) < 12: + examples.append((chassis, key, sval, gval)) + extra = [k for k in gkv if k not in skv] + if extra: + page_ok = False + for k in extra: + mismatched_keys["EXTRA:" + k] += 1 + t["pages_ok"] += page_ok + + print(f"chassis verified : {t['chassis']}") + print(f"pages compared : {t['pages']} fully exact: {t['pages_ok']}") + print(f"keys compared : {t['keys']} exact: {t['keys_ok']}" + f" wrong: {t['keys'] - t['keys_ok']}") + if mismatched_keys: + print("\nmismatches by key:") + for k, v in mismatched_keys.most_common(12): + print(f" {k:22s} x{v}") + if examples: + print("\nexamples (chassis, key, source, decompiled):") + for e in examples: + print(f" {e[0]:14s} {e[1]:18s} src={e[2]!r:28s} got={e[3]!r}") + if stale: + print("\nskipped - source has moved on since the package was built:") + for ch, g, s in stale: + print(f" {ch:16s} messages={g} sourcePages={s}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/diffindex.py b/MW4COMPARE/tools/diffindex.py new file mode 100755 index 00000000..f0493382 --- /dev/null +++ b/MW4COMPARE/tools/diffindex.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +""" +diffindex.py - case-insensitive package/entry diff between two mw4index manifests. + + python3 diffindex.py [labelA] [labelB] + +Per package it prints: + * name-set diff - which source assets exist on each side (the reliable signal) + * blob multiset - stored-byte md5 counts, names ignored (a rough upper bound + on how much payload differs; see the caveat in mw4index.py) + +Packages present on only one side are reported as A-ONLY / B-ONLY. +""" +import sys, collections + + +def load(path): + names = collections.defaultdict(set) + blobs = collections.defaultdict(collections.Counter) + sizes = collections.defaultdict(dict) + with open(path, encoding="latin-1") as fh: + for line in fh: + line = line.rstrip("\n") + if not line: + continue + pkg, _rid, name, dlen, _rlen, h = line.split("\t") + pkg = pkg.lower() + key = name.lower().replace("\\", "/") + names[pkg].add(key) + blobs[pkg][h] += 1 + sizes[pkg][key] = int(dlen) + return names, blobs, sizes + + +def main(): + if len(sys.argv) < 3: + sys.exit(__doc__) + la = sys.argv[3] if len(sys.argv) > 3 else "A" + lb = sys.argv[4] if len(sys.argv) > 4 else "B" + an, ab, asz = load(sys.argv[1]) + bn, bb, bsz = load(sys.argv[2]) + + for pkg in sorted(set(an) | set(bn)): + if pkg not in an: + print(f"== {pkg}: {lb}-ONLY PACKAGE ({len(bn[pkg])} entries)") + continue + if pkg not in bn: + print(f"== {pkg}: {la}-ONLY PACKAGE ({len(an[pkg])} entries)") + continue + aonly = sorted(an[pkg] - bn[pkg]) + bonly = sorted(bn[pkg] - an[pkg]) + shared = sum((ab[pkg] & bb[pkg]).values()) + print(f"== {pkg}: {la}={len(an[pkg])} {lb}={len(bn[pkg])} | " + f"names: common={len(an[pkg] & bn[pkg])} {la}_only={len(aonly)} {lb}_only={len(bonly)} | " + f"blobs: identical={shared} {la}_uniq={sum(ab[pkg].values()) - shared} " + f"{lb}_uniq={sum(bb[pkg].values()) - shared}") + for n in aonly: + print(f" +{la} {n} ({asz[pkg][n]}B)") + for n in bonly: + print(f" -{lb} {n} ({bsz[pkg][n]}B)") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/dumprec.py b/MW4COMPARE/tools/dumprec.py new file mode 100755 index 00000000..030207dc --- /dev/null +++ b/MW4COMPARE/tools/dumprec.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +""" +dumprec.py - extract one record from a *.mw4, decoded. + + python3 dumprec.py "" [outfile] + +The entry name is matched case-insensitively with backslashes normalised to +'/', e.g. "mechs/atlas/atlas.subsystems". With no outfile the bytes go to +stdout. Pass "--list" as the entry name to print every entry name instead. +""" +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from mw4db import read_records, norm + + +def main(): + if len(sys.argv) < 3: + sys.exit(__doc__) + path, want = sys.argv[1], norm(sys.argv[2]) + _content, recs = read_records(path) + if want == "--list": + for rid, name, dlen, rlen, _blob in recs: + print(f"{rid}\t{name}\t{dlen}\t{rlen}") + return + for _rid, name, _dlen, _rlen, blob in recs: + if norm(name) == want: + if len(sys.argv) > 3: + open(sys.argv[3], "wb").write(blob) + else: + sys.stdout.buffer.write(blob) + return + sys.exit(f"not found: {want}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/extract-all.py b/MW4COMPARE/tools/extract-all.py new file mode 100644 index 00000000..3c0d4e17 --- /dev/null +++ b/MW4COMPARE/tools/extract-all.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +""" +extract-all.py - unpack every *.mw4 under a resource root into a real directory tree. + + python3 extract-all.py [--no-merged] + +Layout +------ + // + +e.g. core.mw4 entry mechs\\atlas\\atlas.subsystems + -> /core/mechs/atlas/atlas.subsystems + + Missions/freezer.mw4 entry missions\\freezer\\freezer.contents + -> /Missions/freezer/missions/freezer/freezer.contents + +This is lossless: 842 entry paths are claimed by more than one package (281 of +them with different content - typically a mission packing its own copy of a +global props.mw4 asset), so a single flat tree cannot represent the data. + +/_merged/ is then built as a flattened view of the whole set using +HARDLINKS (no extra disk). It mirrors the layout of Gameleap/mw4/Content, which +makes it directly diffable against our source tree. Where two packages disagree, +precedence is props > core > textures > maps > missions and every conflict is +listed in _conflicts.tsv. + +Entry-name qualifiers are preserved verbatim in the filename: + foo.data{gamemodel} foo.contents[joint_hip]{armature} bar.tga{hint} +All characters used by these packages are legal on Linux ('!' '#' '$' '%' '^' +appear in lobby skin names; no ':' or NUL). +""" +import sys, os, hashlib + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from mw4db import read_records, walk_packages + +# Higher wins when the same entry path comes from several packages. +PRECEDENCE = {"props": 50, "core": 40, "textures": 30, "maps": 20, "missions": 10} + +# Saved mechlab loadouts and pilot options name their records '{Mech}', +# '{Subsystem}' etc. with no directory part, so they have no place in a +# Content-shaped tree - 797 variants would all collide on the same few names. +MERGE_EXCLUDE = ("variants/", "pilots/") + + +def in_merged(pkgrel): + return not pkgrel.lower().startswith(MERGE_EXCLUDE) + + +def precedence(pkgrel): + head = pkgrel.split("/")[0].lower() + if head.endswith(".mw4"): + head = head[:-4] + return PRECEDENCE.get(head, 0) + + +def entry_path(name): + """Entry name -> relative filesystem path. Qualifiers kept as-is.""" + p = name.replace("\\", "/").strip("/") + # Defensive: never let an entry escape the output directory. + parts = [seg for seg in p.split("/") if seg not in ("", ".", "..")] + return "/".join(parts) if parts else "_unnamed" + + +def main(): + args = [a for a in sys.argv[1:] if not a.startswith("--")] + if len(args) != 2: + sys.exit(__doc__) + root, out = args + want_merged = "--no-merged" not in sys.argv + + os.makedirs(out, exist_ok=True) + manifest = open(os.path.join(out, "_manifest.tsv"), "w", encoding="utf-8") + manifest.write("package\trecordId\tentryName\tdataLen\trecLen\tmd5\toutPath\n") + notes = open(os.path.join(out, "_notes.txt"), "w", encoding="utf-8") + + # merged bookkeeping: mergedRelPath -> (precedence, pkgrel, md5, absSourceFile) + best = {} + seen_paths = {} # mergedRelPath -> {md5: [pkgrel, ...]} + total_files = total_bytes = 0 + packages = 0 + + for path, pkgrel in walk_packages(root): + res = read_records(path) + if res is None: + notes.write(f"SKIP not-a-#VBD-package: {pkgrel}\n") + continue + _content, recs = res + packages += 1 + pkgdir = os.path.join(out, pkgrel[:-4] if pkgrel.lower().endswith(".mw4") else pkgrel) + written = {} # relPath -> md5, for intra-package dupes + + for rid, name, dlen, rlen, blob in recs: + rel = entry_path(name) + h = hashlib.md5(blob).hexdigest() + + if rel in written: # duplicate entry name in one package + if written[rel] == h: + notes.write(f"DUP-IDENTICAL {pkgrel}\t{name}\trec{rid}\n") + continue + rel = f"{rel}#rec{rid}" + notes.write(f"DUP-DIFFERENT {pkgrel}\t{name}\trec{rid} -> {rel}\n") + written[rel] = h + + dest = os.path.join(pkgdir, rel) + os.makedirs(os.path.dirname(dest), exist_ok=True) + with open(dest, "wb") as fh: + fh.write(blob) + total_files += 1 + total_bytes += len(blob) + manifest.write(f"{pkgrel}\t{rid}\t{name}\t{dlen}\t{rlen}\t{h}\t" + f"{os.path.relpath(dest, out)}\n") + + if want_merged and in_merged(pkgrel): + seen_paths.setdefault(rel, {}).setdefault(h, []).append(pkgrel) + pr = precedence(pkgrel) + cur = best.get(rel) + if cur is None or pr > cur[0]: + best[rel] = (pr, pkgrel, h, dest) + + print(f" {pkgrel}: {len(recs)} records", flush=True) + + manifest.close() + + conflicts = 0 + if want_merged: + mroot = os.path.join(out, "_merged") + with open(os.path.join(out, "_conflicts.tsv"), "w", encoding="utf-8") as cf: + cf.write("mergedPath\tchosenPackage\tallVersions\n") + for rel, (_pr, pkgrel, _h, src) in best.items(): + dest = os.path.join(mroot, rel) + os.makedirs(os.path.dirname(dest), exist_ok=True) + if os.path.lexists(dest): + os.unlink(dest) + os.link(src, dest) + versions = seen_paths[rel] + if len(versions) > 1: + conflicts += 1 + detail = "; ".join(f"{h[:8]}={','.join(ps)}" for h, ps in versions.items()) + cf.write(f"{rel}\t{pkgrel}\t{detail}\n") + + notes.write(f"\npackages={packages} files={total_files} bytes={total_bytes} " + f"mergedPaths={len(best)} conflicts={conflicts}\n") + notes.close() + print(f"\npackages : {packages}") + print(f"files : {total_files}") + print(f"bytes : {total_bytes/1e9:.2f} GB") + if want_merged: + print(f"merged : {len(best)} paths, {conflicts} with conflicting versions") + print(f"out : {out}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/mw4db.py b/MW4COMPARE/tools/mw4db.py new file mode 100755 index 00000000..1ae31472 --- /dev/null +++ b/MW4COMPARE/tools/mw4db.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +""" +mw4db.py - reader for GameOS "#VBD" resource packages (*.mw4). + +Format (from mw4\\Libraries\\stuff\\Database.cpp): + + header (20 bytes, little-endian) + char[4] tag "#VBD" + u32 format + u32 contentVersion (63 for this codebase, VER_CONTENTVERSION) + u32 indexSize byte offset of the payload area + u16 recordCount + u16 nextId + + then `recordCount` directory records, packed, no alignment: + s64 FILETIME when this entry was last (re)packed + u32 dataLength decompressed size + u32 recLength stored size + u32 dataOffset offset from `indexSize` + u16 recordId + u8 nameLength + char[] name (latin-1, backslash-separated, may carry + {qualifier} / [page] suffixes) + + payload at indexSize + dataOffset, `recLength` bytes. + Storage rule (Database.cpp:451): recLength == dataLength -> raw, + otherwise LZW-compressed with gos_LZCompress. + +The decompressor is a faithful port of gos_LZDecompress from +CoreTech\\Libraries\\GameOS\\FileIO.cpp - variable-width LZW, LSB-first, +9 -> 12 bits, CLEAR = 256, EOF = 257, dictionary starts at 258. +""" +import struct, hashlib, os + + +def lz_decompress(src: bytes) -> bytes: + src = bytes(src) + b"\x00\x00\x00\x00" # u32-read padding margin + bitpos = 0 + + def getcode(width, mask): + nonlocal bitpos + i = bitpos >> 3 + v = src[i] | (src[i + 1] << 8) | (src[i + 2] << 16) | (src[i + 3] << 24) + v = (v >> (bitpos & 7)) & mask + bitpos += width + return v + + CLEAR, EOF, FREE = 256, 257, 258 + chain = [0] * 8192 + suffix = [0] * 8192 + width, mask, maxindex, free = 9, 511, 512, FREE + oldchain = oldsuffix = 0 + out = bytearray() + + while True: + code = getcode(width, mask) + if code == EOF: + break + if code == CLEAR: + width, mask, maxindex, free = 9, 511, 512, FREE + code = getcode(width, mask) # ClearHash emits one literal + out.append(code & 0xFF) + oldchain, oldsuffix = code, code & 0xFF + continue + + stack = [] + if code >= free: # KwKwK special case + stack.append(oldsuffix) + suffix[code] = oldsuffix + chain[code] = oldchain + walk = oldchain + else: + walk = code + while walk >= 256: + stack.append(suffix[walk]) + walk = chain[walk] + stack.append(walk) + oldsuffix = walk & 0xFF + out += bytes(reversed(stack)) + + suffix[free] = oldsuffix + chain[free] = oldchain + free += 1 + oldchain = code + if free == maxindex and width != 12: + width += 1 + maxindex <<= 1 + mask = (mask << 1) | 1 + return bytes(out) + + +def read_index(path): + """Parse directory only. Fast - no decompression. + + Returns (contentVersion, [(recordId, name, dataLen, recLen, storedBytes), ...]) + or None when the file is not a #VBD package. + """ + with open(path, "rb") as fh: + data = fh.read() + if data[:4] != b"#VBD": + return None + _tag, _fmt, content, indexsize, nrec, _nextid = struct.unpack_from("<4sIIIHH", data, 0) + out, off = [], 20 + for _ in range(nrec): + off += 8 # FILETIME + dlen, rlen, doff = struct.unpack_from(" str: + """Canonical comparison key for an entry name. + + Package entry names are case-inconsistent between builds (V4H's packer + lowercased everything) and use backslashes. Always compare through this. + """ + return name.lower().replace("\\", "/") + + +def md5(b: bytes) -> str: + return hashlib.md5(b).hexdigest() + + +def walk_packages(root): + """Yield (absolutePath, relativePathWithForwardSlashes) for every *.mw4 under root.""" + for dirpath, _dirs, files in os.walk(root): + for f in sorted(files): + if f.lower().endswith(".mw4"): + p = os.path.join(dirpath, f) + yield p, os.path.relpath(p, root).replace("\\", "/") diff --git a/MW4COMPARE/tools/mw4index.py b/MW4COMPARE/tools/mw4index.py new file mode 100755 index 00000000..b9dc4b4b --- /dev/null +++ b/MW4COMPARE/tools/mw4index.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +""" +mw4index.py - fast TSV manifest of every *.mw4 package under a directory. + + python3 mw4index.py > manifest.tsv + +Columns: pkgRelPath \t recordId \t entryName \t dataLen \t recLen \t md5(storedBytes) + +Directory-only, so a 780 MB resource tree indexes in about a second. + +CAUTION: md5 of the *stored* bytes is NOT a reliable equality test. Two builds +that packed identical source can still produce different stored bytes (raw vs +LZW selection, dictionary state). Use it only as a cheap "definitely identical" +signal; confirm real differences with pkgcmp.py, which decodes. +""" +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from mw4db import read_index, walk_packages, md5 + + +def main(): + if len(sys.argv) != 2: + sys.exit(__doc__) + root = sys.argv[1] + for path, rel in walk_packages(root): + try: + r = read_index(path) + except Exception as e: # truncated / unreadable package + print(f"!ERR\t{rel}\t{e}", file=sys.stderr) + continue + if r is None: + print(f"!NOTVBD\t{rel}", file=sys.stderr) + continue + _content, recs = r + for rid, name, dlen, rlen, raw in recs: + print(f"{rel}\t{rid}\t{name}\t{dlen}\t{rlen}\t{md5(raw)}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/pkgcmp.py b/MW4COMPARE/tools/pkgcmp.py new file mode 100755 index 00000000..62b546f1 --- /dev/null +++ b/MW4COMPARE/tools/pkgcmp.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +""" +pkgcmp.py - authoritative content diff of two versions of the same *.mw4. + + python3 pkgcmp.py + +Decodes every record on both sides and compares the *decompressed* bytes, so +the result is free of packer noise. This is the only trustworthy equality test; +stored-byte comparison badly over-reports (peaks.mw4: 9 stored-byte diffs, 1 +real one). + +Throughput is roughly 2 MB/s of package (props.mw4, 114 MB, takes ~50 s). +""" +import sys, os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from mw4db import read_records, norm, md5 + + +def load(path): + _content, recs = read_records(path) + return {norm(name): (dlen, md5(blob), blob) for _rid, name, dlen, _rlen, blob in recs} + + +def main(): + if len(sys.argv) != 3: + sys.exit(__doc__) + a, b = load(sys.argv[1]), load(sys.argv[2]) + aonly = sorted(set(a) - set(b)) + bonly = sorted(set(b) - set(a)) + diff = sorted(k for k in set(a) & set(b) if a[k][1] != b[k][1]) + print(f"# entries: A={len(a)} B={len(b)} " + f"A_only={len(aonly)} B_only={len(bonly)} decoded-differ={len(diff)}") + for k in aonly: + print(f"+A {k} {a[k][0]}B") + for k in bonly: + print(f"-B {k} {b[k][0]}B") + for k in diff: + print(f"~ {k} A={a[k][0]}B B={b[k][0]}B") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/prune-identical.py b/MW4COMPARE/tools/prune-identical.py new file mode 100644 index 00000000..1167fe7a --- /dev/null +++ b/MW4COMPARE/tools/prune-identical.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +""" +prune-identical.py - delete everything from an extracted tree that we already have, +leaving only genuine differences and additions. + + python3 prune-identical.py [--apply] + +Dry-run by default; nothing is deleted without --apply. + +Why two baselines +----------------- +A .mw4 is not a zip of the source tree. Our own packer rewrites ~7,400 files on +the way in (.data .video .instance .contents .audio .damage .torso .subsystems +.engine .lights) and generates a further ~17,700 qualified records that have no +source file at all ('foo.data{gamemodel}', 'foo.contents[joint_hip]{armature}', +'bar.tga{hint}'). Comparing only against Content/ would therefore "keep" about +24,000 files we in fact already have. + +So each candidate is tested, in order: + + 1. same package + same entry path in OUR extracted packages, identical bytes + 2. same entry path anywhere in OUR extracted packages, identical bytes + 3. unqualified name + same path in our Content/ source tree, identical bytes + +Any hit means we have that exact asset -> delete. Rule 3 is what recognises +raw pass-through assets (.tga, .wav, .erf, scripts); rules 1-2 are what +recognise the packer-generated forms. + +Afterwards _merged/ is rebuilt from the survivors and empty directories removed. +""" +import sys, os, hashlib, collections, shutil + +OURS_EXTRACTED = "/home/rich/Repositories/FS_Ours_extracted" +OUR_SOURCE = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content" + +PRECEDENCE = {"props": 50, "core": 40, "textures": 30, "maps": 20, "missions": 10} +MERGE_EXCLUDE = ("variants/", "pilots/") + + +def digest(path, cache={}): + key = (path, os.path.getsize(path)) + if key not in cache: + with open(path, "rb") as fh: + cache[key] = hashlib.md5(fh.read()).hexdigest() + return cache[key] + + +def index_paths(root, skip_top=()): + """relative-lowercased-path -> absolute path. No hashing (lazy).""" + out = {} + for dp, dirs, fs in os.walk(root): + if dp == root: + dirs[:] = [d for d in dirs if d.lower() not in skip_top] + for f in fs: + p = os.path.join(dp, f) + out[os.path.relpath(p, root).replace("\\", "/").lower()] = p + return out + + +def package_of(rel): + """Split '/' for an extracted file. + + Package directories are: core, props, textures, maps/, Missions/, + Variants/, Pilots/Tesla/options + """ + parts = rel.split("/") + head = parts[0].lower() + if head in ("core", "props", "textures"): + return parts[0], "/".join(parts[1:]) + if head in ("maps", "missions", "variants") and len(parts) > 2: + return "/".join(parts[:2]), "/".join(parts[2:]) + if head == "pilots" and len(parts) > 3: + return "/".join(parts[:3]), "/".join(parts[3:]) + return parts[0], "/".join(parts[1:]) + + +def merged_precedence(pkg): + return PRECEDENCE.get(pkg.split("/")[0].lower(), 0) + + +def main(): + if len(sys.argv) < 2: + sys.exit(__doc__) + root = sys.argv[1] + apply_ = "--apply" in sys.argv + merged_root = os.path.join(root, "_merged") + + print("indexing our extracted packages ...", flush=True) + ours_pkg = index_paths(OURS_EXTRACTED, skip_top={"_merged"}) + ours_by_entry = {} + for rel, p in ours_pkg.items(): + if rel.startswith("_"): + continue + _pkg, entry = package_of(rel) + ours_by_entry.setdefault(entry, []).append(p) + print(f" {len(ours_pkg)} files, {len(ours_by_entry)} distinct entry paths") + + print("indexing our Content source tree ...", flush=True) + src = index_paths(OUR_SOURCE) + print(f" {len(src)} files") + + stats = collections.Counter() + doomed, kept = [], [] + + for dp, _dirs, fs in os.walk(root): + if dp == merged_root or dp.startswith(merged_root + os.sep): + continue + for f in fs: + p = os.path.join(dp, f) + rel = os.path.relpath(p, root).replace("\\", "/") + if rel.startswith("_"): + continue + pkg, entry = package_of(rel) + h = digest(p) + reason = None + + cand = ours_pkg.get(rel.lower()) + if cand and digest(cand) == h: + reason = "same-package" + if reason is None: + for c in ours_by_entry.get(entry.lower(), ()): + if digest(c) == h: + reason = "other-package" + break + if reason is None and "{" not in f and "[" not in f: + c = src.get(entry.lower()) + if c and digest(c) == h: + reason = "our-source" + + if reason: + stats["deleted:" + reason] += 1 + doomed.append((rel, reason)) + else: + stats["kept"] += 1 + kept.append((rel, pkg, entry, h)) + + total = len(doomed) + len(kept) + print(f"\nexamined {total} files") + for k, v in sorted(stats.items()): + print(f" {v:7d} {k}") + print(f"\n -> would delete {len(doomed)}, keep {len(kept)}") + + with open(os.path.join(root, "_survivors.tsv"), "w", encoding="utf-8") as fh: + fh.write("path\tpackage\tentry\tmd5\n") + for rel, pkg, entry, h in sorted(kept): + fh.write(f"{rel}\t{pkg}\t{entry}\t{h}\n") + + if not apply_: + print(f"\nDRY RUN - nothing changed. Survivor list written to " + f"{os.path.join(root, '_survivors.tsv')}. Re-run with --apply.") + return + + for rel, _reason in doomed: + os.remove(os.path.join(root, rel)) + + with open(os.path.join(root, "_pruned.tsv"), "w", encoding="utf-8") as fh: + fh.write("deletedPath\tmatchedVia\n") + for rel, reason in sorted(doomed): + fh.write(f"{rel}\t{reason}\n") + + # rebuild _merged from survivors + shutil.rmtree(merged_root, ignore_errors=True) + best = {} + for rel, pkg, entry, h in kept: + if pkg.lower().startswith(MERGE_EXCLUDE): + continue + pr = merged_precedence(pkg) + cur = best.get(entry.lower()) + if cur is None or pr > cur[0]: + best[entry.lower()] = (pr, entry, os.path.join(root, rel)) + for _pr, entry, srcfile in best.values(): + dest = os.path.join(merged_root, entry) + os.makedirs(os.path.dirname(dest), exist_ok=True) + os.link(srcfile, dest) + + removed = 0 + for dp, dirs, fs in os.walk(root, topdown=False): + if dp == root or not os.path.isdir(dp): + continue + if not os.listdir(dp): + os.rmdir(dp) + removed += 1 + + print(f"\ndeleted {len(doomed)} files, removed {removed} empty directories") + print(f"_merged rebuilt with {len(best)} paths") + print(f"log: {os.path.join(root, '_pruned.tsv')}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/restructure.py b/MW4COMPARE/tools/restructure.py new file mode 100644 index 00000000..5f7aeee0 --- /dev/null +++ b/MW4COMPARE/tools/restructure.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +""" +restructure.py - reshape an extracted tree to match the repo's directory layout. + + python3 restructure.py [--apply] + +Dry-run by default. + +The extractor writes one directory per package, which is lossless but does not +look like the repo. This flattens it to the shape of Gameleap/mw4/Content: + + /Content/Mechs/Champion/champion.subsystems + /Content/Maps/alpine02/... + /Resource/Variants//... (variant records have no path) + /Resource/Pilots/Tesla/options/... + /_pkgroots/... (4-byte package-root pseudo-entries) + +Path components are re-cased to match the repo wherever a counterpart exists, so +'mechs/longbow/longbow.data{element}' becomes +'Content/Mechs/Longbow/longbow.data{Element}'. Components with no counterpart +(the new chassis, for instance) keep the name the packer used. + +REFUSES TO RUN if two packages would land on the same path with different bytes. +Flatten only trees where that has been checked - the pruned V4H tree has zero +collisions; our own full extraction has 60 (mission-local copies of shared +Culturals props) and must stay per-package. + +Provenance is preserved in _layout.tsv: newPath, package, original entry path. +""" +import sys, os, hashlib, collections + +REPO_CONTENT = "/home/rich/Repositories/firestorm/Gameleap/mw4/Content" + + +def build_case_map(root): + """lowercased relative path -> the repo's actual spelling, for dirs and files.""" + out = {} + for dp, dirs, files in os.walk(root): + rel = os.path.relpath(dp, root).replace("\\", "/") + base = "" if rel == "." else rel + for name in dirs + files: + r = f"{base}/{name}" if base else name + out[r.lower()] = r + return out + + +def recase(path, case_map): + """Re-case each component against the repo, keeping unknown ones as-is.""" + done = [] + for part in path.split("/"): + probe = "/".join(done + [part]).lower() + canonical = case_map.get(probe) + done.append(canonical.rsplit("/", 1)[-1] if canonical else part) + return "/".join(done) + + +def package_of(rel): + parts = rel.split("/") + head = parts[0].lower() + if head in ("core", "props", "textures"): + return parts[0], "/".join(parts[1:]) + if head in ("maps", "missions", "variants") and len(parts) > 2: + return "/".join(parts[:2]), "/".join(parts[2:]) + if head == "pilots" and len(parts) > 3: + return "/".join(parts[:3]), "/".join(parts[3:]) + return parts[0], "/".join(parts[1:]) + + +def main(): + if len(sys.argv) < 2: + sys.exit(__doc__) + root = sys.argv[1] + apply_ = "--apply" in sys.argv + + print("indexing repo Content/ for canonical casing ...", flush=True) + case_map = build_case_map(REPO_CONTENT) + print(f" {len(case_map)} path components") + + plan = [] # (oldRel, newRel, package, entry) + claims = collections.defaultdict(dict) # newRel -> md5 -> [package] + + for dp, dirs, fs in os.walk(root): + if dp == root: + dirs[:] = [d for d in dirs if d not in ("_merged", "Content", "Resource", "_pkgroots")] + for f in fs: + old = os.path.relpath(os.path.join(dp, f), root).replace("\\", "/") + if old.startswith("_"): + continue + pkg, entry = package_of(old) + low = pkg.lower() + if low.startswith("variants/"): + new = f"Resource/{pkg}/{entry}" + elif low.startswith("pilots/"): + new = f"Resource/{pkg}/{entry}" + elif entry.lower().startswith("resource/"): + new = f"_pkgroots/{pkg}/{entry}" + else: + new = "Content/" + recase(entry, case_map) + plan.append((old, new, pkg, entry)) + if new.startswith("Content/"): + with open(os.path.join(root, old), "rb") as fh: + h = hashlib.md5(fh.read()).hexdigest() + claims[new].setdefault(h, []).append(pkg) + + clashes = {k: v for k, v in claims.items() if len(v) > 1} + dupes = {k: v for k, v in claims.items() + if len(v) == 1 and len(next(iter(v.values()))) > 1} + + print(f"\n{len(plan)} files -> {len({n for _o, n, _p, _e in plan})} destinations") + print(f" identical copies from several packages : {len(dupes)}") + print(f" CONFLICTING copies (different bytes) : {len(clashes)}") + if clashes: + for k, v in list(clashes.items())[:10]: + print(f" {k} " + "; ".join(f"{h[:6]}={','.join(p)}" for h, p in v.items())) + sys.exit("\nrefusing to flatten: resolve the conflicts first") + + sample = [(o, n) for o, n, _p, _e in plan if o != n][:8] + print("\nsample:") + for o, n in sample: + print(f" {o}\n -> {n}") + + if not apply_: + print("\nDRY RUN - nothing changed. Re-run with --apply.") + return + + import shutil + shutil.rmtree(os.path.join(root, "_merged"), ignore_errors=True) + + moved, deduped = 0, 0 + with open(os.path.join(root, "_layout.tsv"), "w", encoding="utf-8") as fh: + fh.write("newPath\tpackage\toriginalEntry\n") + for old, new, pkg, entry in sorted(plan): + src, dest = os.path.join(root, old), os.path.join(root, new) + os.makedirs(os.path.dirname(dest), exist_ok=True) + if os.path.exists(dest): # identical copy from another package + os.remove(src) + deduped += 1 + else: + os.replace(src, dest) + moved += 1 + fh.write(f"{new}\t{pkg}\t{entry}\n") + + removed = 0 + for dp, _dirs, _fs in os.walk(root, topdown=False): + if dp == root or not os.path.isdir(dp): + continue + if not os.listdir(dp): + os.rmdir(dp) + removed += 1 + + print(f"\nmoved {moved}, dropped {deduped} identical duplicates, " + f"removed {removed} empty directories") + print(f"provenance: {os.path.join(root, '_layout.tsv')}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/split-source.py b/MW4COMPARE/tools/split-source.py new file mode 100644 index 00000000..3df52fc9 --- /dev/null +++ b/MW4COMPARE/tools/split-source.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +""" +split-source.py - separate repackable source files from compiled records, and +rename the ones that need it, so the tree can be dropped into Content/. + + python3 split-source.py [--apply] + +Dry-run by default. + +The problem +----------- +A .mw4 does not store the source tree. Some records are the source file byte for +byte; others are what the packer produced from it. Classification was derived +empirically by extracting OUR OWN packages and hash-matching every record against +Gameleap/mw4/Content (22,138 matched, 28,728 did not): + + verbatim source .tga .erf .mw4anim .bid .wav .bsp .material .abl .obb .ebf + .bounds .animscript .fgd .tcf .mlr .d3f .gaf .script .h .abi + compiled .data .instance .subsystems .damage .torso .engine .audio + .video .lights .mw4, and every {hint} {handle} {gamemodel} + {element} {footsteps} {zones} {nametable} [shadow] + [joint_*]{armature} [joint_*]{sites} record + +Worth being concrete: our source Content/Mechs/Atlas/atlas.data is 4,891 bytes of +text; the record packed under that name is a 12-byte binary stub. The real +payload went into the {gamemodel}/{element} records. So a mech's .data .damage +.subsystems .instance CANNOT be recovered from a package - they have to be +re-authored. + +.armature likewise does not exist in a package. The packer splits it into +per-joint X.contents[joint_*]{armature} records, which are compiled. + +What this does +-------------- + * X.data{solidobb} -> X_skeleton_SOLID.obb (mechs) + X.data{hierarchicalobb} -> X_skeleton.obb (mechs) + ...or _SOLID.obb / .obb outside Content/Mechs. These really are .obb files + ('#BBO' magic, headers byte-identical to ours). The exact source spelling is + declared inside the .data text, which we do not have, so a convention is + applied and recorded in _source-vs-compiled.tsv - whatever .data gets + authored later must reference the same name. + * everything classified as compiled moves to _compiled/, keeping its path + * everything left under Content/ is genuine, repackable source + +Resource/ (variants, pilot options) is left alone: those are whole .mw4 +packages, best taken from FS_Build_V4H/resource/Variants/*.mw4 directly rather +than from their unpacked records. +""" +import sys, os, re, collections + +SOURCE_EXT = {".tga", ".erf", ".mw4anim", ".bid", ".wav", ".bsp", ".material", + ".abl", ".obb", ".ebf", ".bounds", ".animscript", ".fgd", ".tcf", + ".mlr", ".d3f", ".gaf", ".h", ".abi", ".hpp", ".include", ".tpl"} +COMPILED_EXT = {".data", ".instance", ".subsystems", ".damage", ".torso", + ".engine", ".audio", ".video", ".lights", ".mw4"} +OBB_QUALS = {"{solidobb}": ("_skeleton_SOLID.obb", "_SOLID.obb"), + "{hierarchicalobb}": ("_skeleton.obb", ".obb")} + +QUAL = re.compile(r'(\[[^\]]*\]|\{[^}]*\})') + + +def is_text(path, limit=8192): + with open(path, "rb") as fh: + chunk = fh.read(limit) + if not chunk: + return False + printable = sum(1 for b in chunk if 9 <= b <= 13 or 32 <= b < 127) + return printable / len(chunk) > 0.90 + + +def classify(path, name, rel): + quals = "".join(QUAL.findall(name)).lower() + stem = QUAL.sub("", name) + ext = os.path.splitext(stem)[1].lower() + + if quals in OBB_QUALS: + mech = "/mechs/" in rel.lower() + suffix = OBB_QUALS[quals][0 if mech else 1] + return "source", stem.rsplit(".", 1)[0] + suffix + if quals: + return "compiled", name + if ext in SOURCE_EXT: + return "source", name + if ext in COMPILED_EXT: + return "compiled", name + # .script and .contents are genuinely mixed - decide on content + return ("source" if is_text(path) else "compiled"), name + + +def main(): + if len(sys.argv) < 2: + sys.exit(__doc__) + root = sys.argv[1] + apply_ = "--apply" in sys.argv + content = os.path.join(root, "Content") + if not os.path.isdir(content): + sys.exit(f"no Content/ in {root} - run restructure.py first") + + plan, stats = [], collections.Counter() + renames = collections.Counter() + for dp, _dirs, fs in os.walk(content): + for f in fs: + p = os.path.join(dp, f) + rel = os.path.relpath(p, root).replace("\\", "/") + verdict, newname = classify(p, f, rel) + new = ("Content/" if verdict == "source" else "_compiled/Content/") + \ + os.path.relpath(os.path.join(dp, newname), content).replace("\\", "/") + plan.append((rel, new, verdict)) + stats[verdict] += 1 + if verdict == "source" and newname != f: + renames[os.path.splitext(f)[1] or f] += 1 + + print(f"Content/: {sum(stats.values())} files") + print(f" repackable source : {stats['source']}") + print(f" compiled records : {stats['compiled']} (-> _compiled/)") + if renames: + print("\nrenamed:") + for k, v in renames.most_common(): + print(f" {v:5d} {k}") + print("\nsample renames:") + shown = 0 + for old, new, v in plan: + if v == "source" and os.path.basename(old) != os.path.basename(new) and shown < 6: + print(f" {os.path.basename(old)} -> {os.path.basename(new)}") + shown += 1 + + if not apply_: + print("\nDRY RUN - nothing changed. Re-run with --apply.") + return + + for old, new, _v in plan: + src, dest = os.path.join(root, old), os.path.join(root, new) + os.makedirs(os.path.dirname(dest), exist_ok=True) + os.replace(src, dest) + + with open(os.path.join(root, "_source-vs-compiled.tsv"), "w", encoding="utf-8") as fh: + fh.write("verdict\toriginalPath\tnewPath\n") + for old, new, v in sorted(plan): + fh.write(f"{v}\t{old}\t{new}\n") + + removed = 0 + for dp, _dirs, _fs in os.walk(root, topdown=False): + if dp == root or not os.path.isdir(dp): + continue + if not os.listdir(dp): + os.rmdir(dp) + removed += 1 + print(f"\nmoved {len(plan)} files, removed {removed} empty directories") + print(f"log: {os.path.join(root, '_source-vs-compiled.tsv')}") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/treediff.py b/MW4COMPARE/tools/treediff.py new file mode 100755 index 00000000..1b554db3 --- /dev/null +++ b/MW4COMPARE/tools/treediff.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +""" +treediff.py - case-insensitive loose-file diff of two directory trees. + + python3 treediff.py [labelA] [labelB] + +Compares by relative path (lowercased, '/'-normalised) and md5. Written for the +hsh/, content/ and root config comparisons, where the two builds disagree on +filename case but not on identity. +""" +import sys, os, hashlib + + +def scan(root): + out = {} + for dirpath, _dirs, files in os.walk(root): + for f in files: + p = os.path.join(dirpath, f) + rel = os.path.relpath(p, root).replace("\\", "/") + with open(p, "rb") as fh: + h = hashlib.md5(fh.read()).hexdigest() + out[rel.lower()] = (os.path.getsize(p), h, rel) + return out + + +def main(): + if len(sys.argv) < 3: + sys.exit(__doc__) + la = sys.argv[3] if len(sys.argv) > 3 else "A" + lb = sys.argv[4] if len(sys.argv) > 4 else "B" + a, b = scan(sys.argv[1]), scan(sys.argv[2]) + aonly = sorted(set(a) - set(b)) + bonly = sorted(set(b) - set(a)) + diff = sorted(k for k in set(a) & set(b) if a[k][1] != b[k][1]) + print(f"# {la}={len(a)} files, {lb}={len(b)} files | " + f"identical={len(set(a) & set(b)) - len(diff)} differ={len(diff)} " + f"{la}_only={len(aonly)} {lb}_only={len(bonly)}") + for k in aonly: + print(f"+{la}\t{a[k][2]}\t{a[k][0]}") + for k in bonly: + print(f"-{lb}\t{b[k][2]}\t{b[k][0]}") + for k in diff: + print(f"~DIF\t{a[k][2]}\t{la}={a[k][0]}B {lb}={b[k][0]}B") + + +if __name__ == "__main__": + main() diff --git a/MW4COMPARE/tools/variants-report.py b/MW4COMPARE/tools/variants-report.py new file mode 100755 index 00000000..d26a371f --- /dev/null +++ b/MW4COMPARE/tools/variants-report.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +""" +variants-report.py - inventory of resource/Variants/*.mw4 (saved mechlab loadouts). + + python3 variants-report.py + +A variant package holds 4 records: + [0] 'Resource\\Variants\\' 4 B (raw id) + [1] '{Mech}' ~350 B + [2] '{Subsystem}' ~4 KB <- names the chassis it needs + [3] '<8-char token>' 16 B + +So the chassis a variant depends on is readable straight from the manifest, +with no decompression: the record whose name ends in '{Subsystem}'. + +The report groups variants by chassis and flags any whose chassis is absent +from the target build's core.mw4 (those will not load). +""" +import sys, os, collections + + +def chassis_in_core(manifest): + """Set of chassis names that core.mw4 defines (one *.subsystems per chassis).""" + out = set() + with open(manifest, encoding="latin-1") as fh: + for line in fh: + pkg, _rid, name, *_ = line.rstrip("\n").split("\t") + if pkg.lower() != "core.mw4": + continue + n = name.lower().replace("\\", "/") + if n.endswith(".subsystems"): + out.add(n.rsplit("/", 1)[-1][:-len(".subsystems")]) + return out + + +def variant_chassis(manifest): + """variantPkgRelPath -> chassis name it requires.""" + out = {} + with open(manifest, encoding="latin-1") as fh: + for line in fh: + pkg, _rid, name, *_ = line.rstrip("\n").split("\t") + if not pkg.lower().startswith("variants/"): + continue + if name.endswith("{Subsystem}"): + out[pkg] = name[:-len("{Subsystem}")].lower() + return out + + +def main(): + if len(sys.argv) != 4: + sys.exit(__doc__) + vdir, v4h_manifest, ours_manifest = sys.argv[1:4] + have_v4h = chassis_in_core(v4h_manifest) + have_ours = chassis_in_core(ours_manifest) + vc = variant_chassis(v4h_manifest) + + print(f"# variant packages: {len(vc)} (files on disk: " + f"{len([f for f in os.listdir(vdir) if f.lower().endswith('.mw4')])})") + print(f"# chassis defined in V4H core.mw4 : {len(have_v4h)}") + print(f"# chassis defined in OUR core.mw4 : {len(have_ours)}") + print(f"# chassis only V4H defines : {sorted(have_v4h - have_ours)}") + print() + + by = collections.defaultdict(list) + for pkg, ch in vc.items(): + by[ch].append(pkg) + + loadable_now = broken = needs_new_mech = 0 + print(f"{'chassis':22s} {'count':>5s} status") + for ch in sorted(by, key=lambda c: (-len(by[c]), c)): + n = len(by[ch]) + if ch in have_ours: + status = "loadable in OUR build today" + loadable_now += n + elif ch in have_v4h: + status = "needs the new chassis ported" + needs_new_mech += n + else: + status = "BROKEN - chassis missing from V4H too" + broken += n + print(f"{ch:22s} {n:5d} {status}") + + print() + print(f"loadable in our build today : {loadable_now}") + print(f"blocked on new chassis : {needs_new_mech}") + print(f"broken / orphaned : {broken}") + for ch in sorted(by): + if ch not in have_v4h: + print(f" orphan chassis '{ch}': " + ", ".join(sorted(by[ch]))) + + +if __name__ == "__main__": + main()