Pinnacle Station 2 Header Banner

On November 7, 2024, the ME3Tweaks group released an update for Pinnacle Station for Mass Effect Legendary Edition. This update aimed to polish the DLC further, as well as make it more fun and engaging. I’d like to go over the changes and technical challenges of its development, as well as some other changes in the scene since my last post. We wrote an article back in 2021 detailing the process of the original port, a worthwhile read before reading this one.

Pinnacle Station

As a recap, the original Pinnacle Station DLC was released back in 2009 by Demiurge Studios, the same porting company that ported the original Mass Effect to PC the year prior. It received mediocre reception at best, being a combat simulator DLC with little story, which was at odds with the strengths of the first game. 

Shepard talking to ochren in the original port

Talking to Ochren in the 2021 release

When BioWare requested the source code from Demiurge after the DLC had been published, it was found that the live copy and the backup copies had been corrupted, which is why the DLC never appeared on any subsequent releases. BioWare chose not to recreate the DLC for Legendary Edition, as it would take significant effort on a project that likely was already behind schedule due to COVID-19.

 

In the later half of 2021, we ported this DLC to Legendary Edition using the modding toolset our development group maintains, Legendary Explorer. We coded a porting pipeline that we named VTest, which is the main part of the Pinnacle Station porting project, which we call CrossGenV. We were able to port it due to our tooling being designed to work on “cooked” game files, whereas BioWare’s editor tools are designed to work on non-cooked game files. Many people like to say ‘BioWare was lazy’ or ‘They could have just done it themselves like the modders did’, but would it really have been worth the time and effort? I don’t think so.

 

Logo for CrossgenV

We, as modders, conveniently don’t do this for a living, so we don’t really have deadlines. We took a few months to do the initial port, and now we’re back with a few more months of work to polish it up a few years later. Let’s go over some of the big changes and the challenges they presented to us back in 2021 and how they compare to today, three years later.

 

We brought a few additional members into the development team for this update – DropTheSquid and NaNuke, both leaders in their respective fields of modding. We also heavily cleaned up the code for VTest, making it an actual project, rather than a few huge files with a couple of methods in them.

Static Lighting

The main goal of Pinnacle Station’s 2.0 update was to improve the performance of the DLC so it felt like a regular DLC and not a mod. In the original release, we marked this mod as requiring higher-end graphics cards (at the time), because the game had to render real time lighting. This type of lighting setup is very performance heavy and is mostly meant for things that move – characters, vehicles, muzzle flashes – etc. As such, for entire level lighting, it doesn’t look very good.

 

Picture of a UDK built lightmap

In Unreal Engine 3, ‘Static lighting’ is precomputed lighting from light sources that don’t move on mesh objects that also don’t move. The lighting for these objects can be precomputed with a tool called Lightmass, which emits ‘photons’ from light sources and calculates the resulting light and shadows, mapping them as textures on static meshes. These are called Lightmaps, and they are very computationally expensive to compute – each of our 5 maps took about 10 minutes each to compute on a 16-core system. As Unreal Engine 3 is from the mid 2000s, it doesn’t use the GPU to compute this.

 

When we originally ported this DLC, we made a system for VTest that we called the ‘donor’ system, which is used when porting assets across games. When an asset with the same name existed in the Legendary Edition of Mass Effect (called LE1), we would port that asset to the destination instead, providing higher fidelity assets and requiring less conversion of data to newer engine formats. Due to how lightmaps are calculated, this yielded many unusable lightmaps, as they no longer correctly mapped onto the meshes.

 

As a result, we opted to use ME1 (2008) versions of some assets rather than the LE1 versions, in order to improve performance. In some cases we could re-use the lightmaps, in others the reduced polygon count improved realtime lighting performance. But some objects looked bad no matter what we did, and for those we choose to use the performance heavy dynamic lighting. Incorrect lightmaps as produce really ugly results, akin to the following:

Overlooking mismatched lightmaps

We manually repainted parts of the Volcanic map’s lightmap hours before launch of the V1 release back in 2021. It greatly increased performance.

 

With the 2.0 update, we no longer use ME1 assets that also exist in LE1, we only use the LE1 versions. In the time since the original release, we figured out how to import Mass Effect maps into UDK without it crashing (well, crashing less). Back in 2022, this proof of concept was shown by mod developer Pietrasgin, and has been used by another developer to create the Hot Labs Restored mod’s lighting.

 

We spent a few weeks debugging UDK so we could export enough of our maps into it that we could compute lighting textures. The lighting quality AND performance would both get a huge benefit from this. But for proper lighting, we would need to have fairly accurate representations of meshes for diffuse photon bouncing. This required us to be able to compile shaders in UDK, which I talk about later in this post.

Left: V2 lighting, Right: V1 lighting

Left: V2 lighting, Right: V1 lighting

 

The static lighting we made for this update is uncompressed (as opposed to DXT1), which uses more memory, but provides a much higher quality light texture. In a few areas we also bumped up the lightmap resolution to provide more accurate lighting and less ‘stair stepping’ effects. The Improved Static Lighting  (ISL) mods exist mostly because of the artifacts produced by DXT1 compression the vanilla game uses, combined with low res lightmaps being used on large meshes.

Low resolution lightmaps on large unpatterned surfaces, combined with DXT1 compression, results in ugly stair-stepping.

 

Interestingly, we found that you don’t want too high of a resolution for a lightmap. A perfect example of this is when we were trying to bake lighting for the apartment you can win. A small overhead bar casts a really ugly shadow with the standard lightmap resolution, but if you crank the resolution too high, it also looks bad, but in a different way.

At the bottom of the shaft shadows you can see them splintering.

 

With a higher resolution lightmap, each emitted photon from light sources becomes more visible. For large objects casting a shadow, this means you get a more detailed shadow. However, for small objects, it means you start to see how each photon coming out of a light source is at a slightly different angle than the one next to it. With a high enough lightmap resolution, you start to see ‘slivers’ of shadows with light between them, which looks terrible.

 

This effect is ultimately why we chose to not make lighting changes to the apartment map, as it is full of these small objects. We plan to revisit this in a future update, but the apartment is complicated due to our early development decisions.

 

Oh, and terrain lightmaps don’t work properly. We kept the same lightmaps for terrains. Terrains are just awful.

PTSD with Shepard laying facedown on rainbow ground

Touching terrain in this broken state instantly kills you and often sends you flying.
So this is an accurate representation of how it feels to work with it.

Shaders

In the original Pinnacle Station DLC blog post, we talked about shaders a bit, saying that we could not port these from ME1 to LE1, something that remains true as of the time this article was written. Shaders are tiny individual programs that are used to draw 3d surfaces, and without them, you can’t draw a 3d object.

 

Since our original release, a few enterprising modders have created a way to enable basic shader editing – using tools that allow us to decompile and recompile DirectX 11 shaders to and from High Level Shader Language (HLSL). This allows us to change shaders in fairly minor ways. For example, changing hard coded colors in the shader, adjusting transparencies, or adding a few effects from other shaders. It’s still pretty tedious, and our editing tools for it currently are very basic – but they’re slowly coming along.

Renderdoc showing a step in the render pipeline

Using RenderDoc to identify which shader applies the effects we want to edit

 

Messing around with the material editor in UDK yielded interesting information – Materials, which are what generate shaders for use in different contexts, generate the information used to compile them and store them in the package files UDK saves. Conveniently, Demiurge shipped a lot of uncooked files in the PC port of ME1, which included all of the material packages. These packages include the material expression data needed to compile shaders. Cooked files strip out most of the expressions, as they are not needed except for compiling shaders.

 

Using the ME1 files, we were able to rebuild most of that game’s shaders in UDK in DirectX 11, the same version of DirectX that the Legendary Edition game uses. These would be the ME1 versions, rather than the LE1 versions, but they are fairly similar.

 

For building lighting, these were sufficient to produce an accurate diffuse, which allowed us to properly bake lighting. Someday in the future, we may be able to port shaders out of UDK and use them in Legendary Edition. But that’s still a ways out from even being able to be tested.

ME1 Water Shader in UDK

UDK showing a water material we ported out of ME1. It’s even animated in the editor.

 

NaNuke, our scene’s expert on shader editing, contributed some modified shaders to CrossGenV to improve a few lower quality donors we used back in 2021. Notably, the capture mode rings being able to be tinted in realtime and the frost on the station glass are two examples of shader editing for our 2.0 update.

 

UDK Tooling

To support baking lightmaps for this update, we had to make significant changes to our Unreal Development Kit (UDK) file support. UDK is the free version of Unreal Engine 3, but it comes with serious limitations that I think would make it difficult to make any real game with – lack of access to engine source code makes debugging anything very difficult.

 

When exporting our game files to UDK, we often would find UDK simply crashed upon opening level files. This instability is why we didn’t dedicate more resources to making UDK a viable editor for many tasks – it often just crashes in native code, and only a select few people in our scene know how to debug native code.

UDK crash log

UDK’s log showing a native crash.

 

With advances in our reverse engineering efforts of Mass Effect Legendary Edition games, we were able to apply that knowledge to UDK to track down crashes. Reverse engineering UDK is both easier and more difficult than the Mass Effect games – it has a lot more debug strings built into it, making it far easier to determine the context of something. But it also uses Address Space Layout Randomization (ASLR), which is really annoying when trying to follow a stack trace.

Scary wall of text

The primary crash we faced came from stricter rules in UDK about how files are laid out.

 

The main issue we faced was not understanding that UDK only supports the concept of exports under exports and imports under imports. All of the Mass Effect trilogy games are fine with putting imports under exports or exports under imports, but UDK does not like that.

 

Imports are just stubs that point to objects that should already exist in memory. When a package is loading, and the import table is enumerated, the engine attempts to resolve them by finding the object in the GObjects table using the full name and class specified by the import. If the object is found, references to the import become references to the found object instead. If the import cannot be resolved, it converts to None, which is Unreal’s version of null. Commonly shared objects are often promoted to a higher tier file (such as a file always loaded in a map), and lower tier files will reference this preloaded object. Imports in package files are indexed using negative values starting at -1.

 

Exports and Imports in a tree

As the export metadata table is enumerated during file load, if a same-named object and class exists in the GObjects table, references to the current parsing export become references to the object already in memory, and the engine skips reading the data of the export, which speeds up file load. Once the file finishes loading, objects that are unreferenced from a level or a pinned object are dumped from memory. Exports are indexed using only positive values starting at 1.

 

In cooked game files, there is a concept of ‘Forced Export’, which means export objects are being forced to live in a different package file. The game is compiled this way so that dependent objects don’t incur seek time on a hard disk drive or a disc. This had a bigger impact back when these games were originally released.

List of Package Exports

 

This concept doesn’t exist in the Unreal editor – imports instead point directly to other package files and will be loaded automatically to fetch object dependencies. All exports reside within that package; package exports at the root of a package file that have the ‘Forced Export’ flag all were standalone .upk files in the game editor. This is also why ME1 (2008) has so much extra unused content in it – those extra uncooked packages include tons of unreferenced data, that in the later cooked games, would never be forced into other files.

 

Legendary Explorer, and before it ME3Explorer, were all coded against the idea of Forced Exports. As such, a very large part of our codebase makes assumptions that it’s fine to pull in data that belongs in a different package, and our codebase struggles to work well with files that don’t use this system, which are often BIOG_* files.

 

Exports under imports, or imports under exports, will cause an immediate crash when the package file loads in UDK. It took significant engineering effort to find a way to export our level files in a way that UDK would accept; in the end, we did not make too many changes to our internal porting code, instead writing specific iterative passes to convert files to be compatible with UDK. This means that using UDK for lightmap baking and map editing now requires ‘decooking’ the game, which moves everything back to standalone packages, a process that takes lots of memory and lots of time.

UDK Editor view of the CCMID part of Pinnacle Station

CCMid, the middle room of Pinnacle Station, loaded in UDK. The little light bulbs are different light sources.

 

Once decooked, UDK accepts our generated packages, and in theory, so would BioWare’s editor. We potentially could fully convert Pinnacle Station back into usable source form for BioWare, though it would take much more effort. We only export a subset of objects to UDK, and our understanding of all object types is still incomplete, let alone what data is stripped during cooking.

 

However, this system isn’t perfect, even for the subset of objects we export. Testing this with some LE3 files to help other mod developers with their static lighting woes resulted in a different crash trying to get surface data when Lightmass begins. For this to be fully reliable we will have to continue work on debugging this conversion process.

 

Load screen updates

In the original Pinnacle Station DLC mod for Legendary Edition, we made load screens that show the view from the observation window of the simulator. We felt these were a nice touch, but we could do better. Showing the simulator actually loading would be quite interesting – and while debugging static lighting, we found that we could see the lighting actually loading when you disabled load screens, when using streamed lightmaps.

Warehouse map shows lighting streaming in over time

The load screens for all simulator maps now show what the level would look like if they were loading without a load screen blocking the view of it. It requires a special streaming lighting build of the mod to generate the same effect.

 

An interesting stretch goal with our more advanced tooling would be to change the materials on all objects to a loading effect, then resetting them a moment later to create a cascading load effect. Maybe someday we’ll experiment with that idea to see what it looks like.

 

Audio updates

Back in 2021, audio editing tooling for both ME1 and LE1 were pretty much non-existent. Both games use ISACT – an absolutely archaic sound engine that very few games appear to have actually used.

 

We have since found a copy of ISACT that matches BioWare’s version number (1.64). After dedicating some time to it in 2023, we developed tools and documentation on how to create new streaming audio (and replace non-streaming to a limited extent). BioWare has a custom fork of the ISACT sound engine, but with minor changes to ISACT’s output files, we can make our files compatible with BioWare’s engine.

ISACT Audio Editor for CrossgenV

ISACT when authoring the new Volcanic music track. It really is a product of it’s time. It crashes on boot if your screen is above 1080p.

We updated music to loop better for the Volcanic map, which provides a much better audio experience than our original track that had a lot of silent and lead-in. We also added enemy soundsets to the Salarian enemies, which provides more audio feedback to combat, with enemies grunting in pain. In the future we hope to expand this to the built-in enemy types to provide a more engaging experience.

 

Enemy Replacements

We wanted to make some functional gameplay changes to Pinnacle Station as well. We strove to ensure you could turn these off if you wanted the ‘vanilla’ experience, but it is no secret that Pinnacle Station often has to compete with Firewalker from ME2 for last place on Mass Effect DLC tier lists.

 

Our original task document listed having Rachni enemies as a ‘Nice to have’, something we never got around to implementing. A large focus for this update was making an extendable system that could provide ‘wavelists’ – lists of enemies that could replace the originals, so you could fight batarians, asari, salarians, or mixes of different enemies.

 

 

Making new enemies for the simulator turned out to be a significant amount of work, not only because the pawn system in the first game is really complicated to use (it’s powerful, but probably far more than necessary), but the game locks files that are dynamically loaded until you restart it. Any change you make to dynamic loaded files requires a game restart, which adds a lot of time. This used to be a big problem in LE2, until we developed the Hot Reload ASI, which changes how files are opened to not lock them.

 

Designing the appearance of the pawns so they were visually unique was a learning experience, as the appearance system is unlike later games. It uses numeric indexes when selecting the mesh and materials (starting at 0), that then translate to asset paths using letters and numerals that are indexed starting at 1/a. These assets must exist in memory or be available in packages as non-forced exports, and meet an exact naming standard.

In order for export 4: GTH_TRO is the asset prefix defined in an appearance, NKD means the ‘Naked’ armor level, a means the 0th mesh, MAT is the asset type, 1 means the 0th material config, and a means it’s the first material to place on the mesh (if more were needed, it would be b, c etc).

 

A big help was DropTheSquid’s Appearance Modification Menu mod, which could be used to preview different assets, making iterating time much faster. You could also make gems like these:

Batarian who can't see due to the helmetBatarian with a headset on in a menu

 

Further complicating this was the first game’s use of the Bio2DA format – which are essentially excel spreadsheets, but used in a database like fashion. You often have to translate what a numeric value actually means mentally, which is error prone and confusing.

 

The component of the mod that actually distributes the enemies into the simulator logic behaves similar to Mass Effect 3 Legendary Edition Randomizer’s (yes, that’s still in development) enemy spawn randomizer, where an object in the Kismet graph modifies other Kismet objects, rather than the objects they point to. You can dynamically modify Kismet, if you like to live dangerously.

 

To enable the player to configure options such as this, we added a new simulator settings to the console on the right side of Ochren. This is a standalone version of the menu system from the Mod Settings Menu component of the LE1 Community Patch. You can tweak settings such as getting XP for first place finishes, enable/disable music, toggle difficulty ramping, and choose from various enemy wavelists. Of note is the ‘Mixed’ wavelist, which can use enemies from all other loaded wavelists, including the original enemies. This was unusually fun to play.

Menu showing different wavelist options

The simulator enemy selector. Developers can add their own wavelists via config files and they will automatically appear here.

 

Difficulty Ramping

With this update, we put a large chunk of development time into difficulty ramping – increasing difficulty over time to make a game mode require more and more player skill to continue playing, while remaining fair for players of lower skill level to complete the base challenge.

 

Pinnacle Station is not too difficult to beat above level 50 with higher level gear (on Veteran). We wanted to ensure it can still provide a fun and engaging challenge that players may want to come back to. In our original release, we had one implementation of difficulty ramping – over time, your healthgate thresholds would decrease, which let enemies do more and more damage even when you had the strongest armor. This was a way to counteract health regeneration and armor making you nearly impossible to kill. We spent a lot of time tuning this to feel fair, and I think we did a great job on it – I haven’t heard people saying it’s too easy or too hard.

 

Given my formative modding years being all based in Mass Effect 3 Multiplayer modding, I wanted to make combat more chaotic and engaging, especially for survival mode. Making bullet sponge enemies is a very boring way of increasing difficulty – which is why I wasn’t really a fan of Platinum difficulty for ME3 – so we added in multiple methods of ramping up difficulty over time.

 

The primary one is having additional enemies spawn over time, which increases player pressure – suddenly your reaction times are shorter and cooldown management becomes very, very important. We put this on a timer in Survival mode and as you complete each capture point in Capture mode.

Graph of kismet showing the seconds until a new spawn point is activated

Kismet sequencing comparing the current match timer to predefined values, then checking if spawn ramping is enabled, and then enabling a respawner if the value is true.

 

But that’s just more vanilla gameplay. I wanted more.

 

Stealing code from LE1Randomizer (yes, that’s still in development), I implemented a way for enemies over time to start gaining inventory mods (such as weapon rails, ammo types, amps, etc) and talents. This is all chance based as enemies spawn in, with the chance going up over time (Survival) or as you capture points (Capture). Adding inventory mods required us to generate new AI classes that would drop their inventory on death, rather than have the player subsume it, which could give you hundreds or thousands of items.

 

This can trigger a steep climb in difficulty, because enemies that roll abilities like singularity, sabotage, or high explosive rounds are extremely dangerous. We made it so the list of talents and powers enemies can be given is configurable in configuration files, so other developers can add to the lists.

Oh yeah, did I mention we can now merge configuration files in LE1 like the other two games?

Configuration file text

Difficulty ramping config file shipped with the 2.0 update.

 

One game mode we really wanted to improve was the Capture game mode, which is very unengaging. The objective is to capture all points as fast as possible – but since the mission is timed, you often just ignore enemies entirely. In the original DLC, touching a capture point often deleted enemies in the vicinity – something we marked as wanting to fix in our original release, but had to cut for time. Enemies also didn’t attempt to close distance, often staying in zones outside of the capture point.

 

We made some functional changes to this game mode, making enemies near the point not automatically despawn when you start to capture it. This appears to have been due to the list of spawnpoints changing to be near the capture point, but it led to a really poor gameplay experience. Enemies that are far from the capture point will still be killed, but they won’t be immediately deleted anymore.

 

Another change was for the capture point indicator and capture ring. We switched to a new material (with a shader modified by NaNuke) that changes over time. This helps provide more visible feedback to the player that they are nearing capture completion. We used our new Asset Viewer feature to load the ring into a level, and then we used (or, tried to) use our live Material Editor tool to preview live changes.

Shepard standing in a ring

Testing out what different parameters of the new shader did.

We also made enemies change their AI to close distance when you touch a capture point. This tends to force you to engage with the enemies more, though it often makes them engage your squadmates instead. LE1’s AI system doesn’t seem to have a way to reliably force a pawn to only target a certain enemy. If we come back to this DLC, we’d probably look into what we can do with the game’s AI.

 

Frameworking

A modding concept new to the Legendary Edition of Mass Effect games is what the scene colloquially calls “Frameworking”. In level files in Mass Effect games, interactable NPCs are often hardcoded into the files. You may see the same NPC in multiple locations, which means that their assets and dependencies are all hardcoded into multiple files. Thus, if you want to change a character’s outfit or other properties, you’d have to edit multiple files. And because level files often contain multiple NPCs and other logic, this gets ugly really quick.

 

The LE3 Community Patch mod introduced the concept of frameworking in 2021, which follows a similar design as the henchman files from BioWare. These files are level files, but each one only contains the NPC. Using a ‘handshake’ system, they can be streamed into a level, and then polled if they are loaded and ready. Once ready, the level file can set them up wherever they’re needed.

Files on the left, Ahern's package on the right.

Frameworking essentially makes new level files, with the only thing in them the NPC, which other files then move around.

 

As part of the Pinnacle Station 2.0 update, we updated VTest to automatically strip out all hardcoded pawns and move them into framework files, so if you want to edit an NPC’s outfit, you only have to edit their file. It improves mod compatibility, and it’s all done in code – we made no manual edits to package files for this feature. We could disable the whole thing just by flipping a boolean in the pipeline.

 

This requires significant changes to the underlying game files, replacing direct references to pawns with soft references, as well as changing some logic to require a completed handshake. We also had to install ‘teleport signals’, which are pieces of logic we hooked up to fire when certain events in game sequencing happened. An example would be teleporting Ahern back to his normal post after telling you ‘Better luck next time’ in another room. If we didn’t teleport him out, he’d just stand around in a room he isn’t supposed to be interactable in.

In the original files, the station had multiple copies of pawns in the areas they could move to, but in our frameworked version, we teleport a single copy around, which required some additional work to ensure correct placement.

New turian model using just a file replacement

New female turian model from LE1 Diversification Project. Done by simply overriding the NPC file for this pawn.

 

I’d like to say the system we built for this would work on other level files, or LE2 – but it requires very heavy analysis of game sequencing to properly set up and use. It’d probably help to start the process, but it likely would require significant effort to replicate elsewhere.

 

Bugfixes + Misc

We made some additional bugfixes and changes for this update as well, many which are minor or not super obvious.

 

Ragdoll teleport

In the original version of the game, as well as our V1 release of this mod, you could get softlocked if you got ragdolled when the game attempted to teleport you out of the simulator map. This could happen for a few reasons – you were rolling down a hill and the game ended, or you got hit by your own grenade after your input got turned off.

 

When we added enemy talents to this update, this issue became much more common, as you could be in ragdoll state much easier, and for longer. After investigating the Kismet teleport action, we found that it would return that it successfully teleported the player, but didn’t actually do anything. Because of this, files that needed to be loaded didn’t, and the flow of game logic essentially hit a dead end, softlocking the game.

The

To remedy this, we made a custom teleport Kismet action, which terminated the player ragdoll and forced them to the new position. This worked great, except you lost the ability to use powers and fire weapons until you got ragdolled again. Ragdolling, like many things in LE1, is all done in native code, which means it’s nearly impossible to work with.

Code for ForceTeleport

The logic for the LEXSeqAct_ForceTeleport kismet class.

 

Writing a few new features into Legendary Explorer’s Script Debugger tool, we were able to dump all properties to a text file, and compare them between for differences on the player pawn. After a bit of experimenting, we located two variables that seem to control your input, a stack and a queue. Resetting these on teleport ensured the player would retain full control. We don’t really know what they mean.

Comparison of two different sets of variables

 

Texture streaming improvements

We put a bit of effort into improving texture streaming around the map, to reduce textures from being blurry on first sight and then becoming more high resolution. About a year ago I wrote a way to calculate a basic TextureToInstancesMap, which the engine uses to preload textures in a certain vicinity. This greatly improved some areas, but didn’t cover everything. We also changed level load to stream in all material textures, but it doesn’t seem to work every time.

 

There’s still improvements to be made. The new frameworked pawns often teleport into view, which doesn’t give textures the time to properly stream in, and we could probably stall the load screen coming down until the game’s streaming system indicates it is no longer loading textures.

 

Polish localization

After years of hearing about various folks getting me permission to do the Polish localization, I finally was contacted by one of the team members of the unofficial localization of Pinnacle Station, who gave us written permission to use it. It may have taken us a bit over a year to get to it, but all text should now be available in Polish, except for the new text we added in 2.0, as it’s still pending full localization.

 

Collision improvements

In our original V1 release, there were a few areas that we knew of that had really bad collision, especially the area to the right of Ochren. If you walked into it, it would teleport you up and right over the wall, and it was incredibly easy to trigger. We spent a bit of time moving the collision boxes to prevent you from being able to do this while still feeling natural. It’s a small change people are unlikely to notice, but once you know it’s there you can’t unknow it.

Gestures on Pawns

Some of the dialogue in the original game caused characters to ‘snap’ to their next line’s animation, such as a fist going into a hand immediately going into hands at their sides.  This DLC is full of problems like this, and while we didn’t fix them all, we fixed some of the more egregious ones, like when you tell Vidinos you don’t have time for sore losers.

 

Known issues

A number of features didn’t get the full level of attention they needed for a clean release, and several features got cut for time, which hopefully we’ll be able to get back to.

 

Music on Warehouse map

The music on the Warehouse map doesn’t play again unless you restart the game after the first time it plays. We aren’t really sure why this does this, because all maps are scripted to get the exact same music triggers. It’s likely the audio bank itself, but we didn’t have time to look more into this. This is also present in the V1 version of our mod.

 

Holowipe effects on enemies

This was a pain in the original release we made, where visors and weapons didn’t have the holographic effect applied to them. We manually fixed things up back then, but now we have an expanded cast of characters to fix, so we’d like to code something up to automatically fix these issues. Our options for fixing this are much better these days, especially with our 2DA merge features.

 

Cut factions

We had basic prototypes done for Asari and Monsterous enemy wavelists, but they got cut for time because they needed a lot more work. We’ll be working on implementing these, with a focus on biotic powers for Asari and… well, we’re not really sure for Monsterous yet.

 

What’s next

We will probably make a few minor bugfix updates for Pinnacle Station 2.0 to address a few of the areas that could use a bit more improvements, and possibly see about baking lighting for the apartment, if we can solve some of the unique challenges it presents.

Level showing the edmonton cheeseburger texture

This year we made Asset Viewer, which lets you preview various game assets. Here we are previewing the legendary Edmonton Cheeseburger texture on the material of the floor mesh of the viewer level.

 

As for the ME3Tweaks group, we’re going to be taking a break and then embarking on another ambitious quest, like we seem to every year.

Reduced Plot Armor: a mod I made that adds a random chance of death, most times in cutscenes where Shepard falls a significant distance, gets hit by a ship, or some other ridiculous over the top event occurs that seems like it could kill you. It’s not quite the Pinnacle Station release of last year, but I’d like to go over some of the unique challenges and design process for this mod, and describe the process that one goes through to design a mod such as this for the Mass Effect trilogy of games.

Development of Reduced Plot Armor started around September 24, 2022, and concluded on November 7, 2022, so it was about a month of on-and-off development.

The development pipeline

When I developed the original Mass Effect Randomizer in 2019, I had to design a system that could apply changes to a very wide set of game files that would be infeasible to do by hand. I developed code to apply a specific set of changes to various tiers of game files – entire packages, specific exports, and custom changes that are specifically targeted, such as randomizing the logic for a specific level gimmick. In Randomizer, these are displayed as options to the user, who can then select which pieces of code will be run.

Options in Mass Effect 3 Randomizer

In Pinnacle Station’s VTest pipeline, we scripted custom changes across a wide set of files, and it allowed us to tweak things and have it propagate to all files without having to do anything manually. This type of pipeline requires a lot of up-front work but it allows me to make iterative changes to my files and have reliable, consistent, and fast results. VTest was a fairly complex pipeline due to the scope of the project at hand.

To be clear, there are very few mods developed this way – most mod developers do everything by hand, as changes often only apply to one file, or would require more up front time to develop the pipeline than it would save in the end. In Reduced Plot Armor, I knew I would be calling the same death chance logic in multiple files, so a pipeline was a good fit for this type of mod. The pipeline for Reduced Plot Armor would need to handle an expanding scope; as opposed to VTest which had a mostly fixed end goal in mind. Things that I had to account for included TLK (localized strings) for each new death chance and patches against mods that mine would override, to name a few.

 

First steps

For Reduced Plot Armor, I knew up front that my changes would be done mostly through Kismet, Unreal Engine 3’s visual scripting system, which is where you have the most control over what gets called. This is how the majority of game sequencing in Mass Effect games is done. In our tooling we call this sequence editing.

An example of Kismet in Mass Effect 3

The first death roll I configured as the prototype was when James crashes the shuttle into the other one at the end of the Mars mission (early in the game). Setting up a blank working mod is easy these days with Starter Kit, a feature of ME3Tweaks Mod Manager. Conveniently Mass Effect 2 and 3 files are named according to their usage (Art, Design, Sound, etc) as well as their position in the level, which makes guessing the correct file much easier. In Mass Effect 1 there is no contextual data, just numbers, which makes finding the file you actually want a pain.

Example of the filesystem in Mass Effect 3

I moved the vanilla version of the game file that contained the cutscene of the shuttle crash scene into my mod’s directory (BioD_ProMar_740Bossfight), and installed the mod into the game, which made the game use my copy of the file instead of the original. DLC mods in Mass Effect games are installed side-by-side original files, loading instead of the originals, rather than entirely replacing the file on disk. This makes it easy to turn mods on and off, override each other, and a few other conveniences at the cost of disk space. Another downside is that certain files that load before DLC is parsed can’t be overridden this way, which are known as basegame-only files.

 

Locating what to change

Now that I had the basic mod set up and installed, I started digging through the sequencing of the file to find a reliable place I could install logic that would trigger a game over condition at the exact right time. In Mass Effect, most in-game (not prerendered) cutscenes use the AnimSequence Kismet object, which exposes an interpolation to variable inputs and outputs. For big cutscenes, they are very easy to spot, typically having their entire own sequence as well as being artpieces themselves in said sequence.

AnimSequence for James’ shuttle crash. The bottom items are the inputs (such as pawns, pieces of the shuttle, etc). And yes, the position data shown is how it looked to BioWare too, for the most part.

In Kismet, things are only executed when the game ticks. Ticking is a periodic update of the game and occurs every time the game renders a new frame. When you pause the game, the game engine suppresses tick events from propagating to most of the gameplay objects, such as pawns, Kismet, etc – but keeps UIs such as the pause menu ticking so they stay responsive.  Ticks include information about how long it has been since the last tick, which AnimSequences use to adjust how far along they are in the animation timeline. As such, you can rely on their timing being accurate most of the time – 30 seconds into an AnimSequence at low and high framerates will be at roughly the same position.

As AnimSequences are not actual Kismet sequences, and are instead a defined set of actions on a timeline – which I don’t have a lot of experience with – I chose the alternate route of simply setting a timer right before the AnimSequence began and then executing my death roll sequence, instead of trying to edit the AnimSequence itself.

The first issue I ran into was finding where I could actually hook this timer up – I wanted only one entrypoint as close to the start of the AnimSequence as possible, but the game often has split logic before these depending on if the player is male or female. I would have to find a place before the logic split to male/female so I could add a new branch for my timer code without having to complicate the pipeline logic.

Gender split logic before an AnimSequence (left column). The middle column is setting the Male and Female Shepard objects that are input into the AnimSequence (not shown), as they each have slightly different animations. There are two additional branches that aren’t shown to the left of this picutre, which are used to determine if Kaidan is present or not.

Ultimately, I chose to activate when the SequenceActivated event is called, which is when the input pin on the entire sequence is triggered. It is only a few objects before the above picture and has only one output, which means I can reliably branch off it.

 

Death roll logic

To make my edits easier, I would make my logic almost all self-contained in a premade sequence that takes inputs that my pipeline will configure when the changes are installed into the file. My pipeline would install a delay of a predefined time before this sequence was run, to make it perform a death roll right when the big moment happens.

The branch for a death roll that my pipeline installs.

Inputs to the RPADeathRoll sequence are the configurable variables for a death roll. As development progressed, additional variables were added and installed by the pipeline. These variables were used to control the feel of the death:

  • DeathMessage: The message to display to the player when the death completes
  • DeathChance: The normal mode death chance, out of 100
  • SlomoSpeed: When the death triggers, this is the percent speed the game will run at. This helps inform the player they died, as well as conveniently hiding the player getting up as the AnimSequence doesn’t stop immediately when the roll occurs
  • HardModeOnly: If this roll should only work when Hard Mode is enabled
  • Ragdoll: Some cutscenes allow you to ragdoll the player, which helps convey that they died. Not all AnimSequences support this – some just make you disappear instead
  • KillPlayer: If the actual gameover condition should be triggered. When this is false, an event is fired instead that can be listened for to trigger my own custom code, which I use for the custom ending I added to this mod

To define each death roll, my pipeline uses a list of RPAEdit objects, which includes information about which file to install to, the object to branch off of, the name of the output link of that object, as well as any non-default values of the inputs to the sequence shown above. My RPAList file contains all definitions, which makes it nice and cleanly separated from the rest of the code.

A single RPAEdit object that defines the inputs.

Designing the logic for the death roll is requires a bit of extra knowledge for how sequences work, such as knowing you need to hook up external variables of the sequence to the correct values in the sequence. Our tooling over the past few years has advanced that it is almost a complete clone of the Kismet Editor BioWare used. Checking if you are current in Hard Mode, if the roll is Hard Mode only, showing the bleedout effect, adjusting game speed and ragdoll, etc., is all is handled in the logic of the roll.

The logic for the death roll, using all the inputs.

Custom logic for death rolls

Some features I wanted my mod to have included showing the player when a roll check succeeded – if you were lucky and made it through the entire game without dying, was it luck? Or maybe the mod wasn’t working?

I designed a few custom Kismet object classes to call other code, such as showing the ‘Your plot armor prevailed’ message, which is not something the vanilla game has a feature for in Kismet. Our tooling improvements over the past 2 years made this a far easier task than it would have been in the years prior to that. In the old days, you’d have to hex edit a class together, which was so unbelievably tedious it had only ever been done a few times. These days you can type up a class just like like you would in Visual Studio, and our editor even has code completion!

The message that shows when your death roll lets you live. An area for potential future improvement is removal of the spinner, so it’s just a fade in an fade out.

Timing it correctly

One area of significant difficulty was slow motion and ragdolling the player – something that the game natively supports, but has issues when combined with a currently playing AnimSequence. Due to the fast paced nature of the cutscenes my mod edits, they often are immediately followed by Shepard and gang getting up, which really hurts the immersion of dying. In Mass Effect, when you trigger a game over condition, there is a 3 second period where the audio drops and changes to the game over music, after which the game pauses and the game over screen is shown. Three seconds in a cutscene is a long time.

Shepard can just get back up if the animation continues and you don’t have a full 3 seconds of them laying around.

My initial solution was to just time the death earlier, but the music dropping out before the actual event occurs made it feel weird and clunky. There were also times where a 3 second window was simply not feasible – Shepard may move their mouth during that window, which obviously signals you didn’t die.

My solution was to slow the game time down in the death roll logic, using a built-in console command called ‘slomo’. This essentially changes the game’s tick rate to a modifier of your choosing. Internally this changes the game’s time dilation, but to keep my code simpler (and have less potential issues) I simply call the slomo command like you can do from the in-game console.

When I initially implemented the slomo effect, I had to sit through nearly 10 seconds of watching Shepard fall and die.

One of many side effects of slowing the game down is that the 3 second gameover timer still runs in game time, which means a 50% slomo yields a 6 second wait until the game over screen, which feels very strange. After a bit of research into how the game sets the timer, I wrote in-game code to modify the the initial value of the timer, stored in the ‘World’ object of the current gameplay session, something you can see on line 15 in the above below. This makes it always time to 3 seconds which makes it feel consistent, and thankfully, appears to be reset to the original value of 3 when a save is reloaded. This variable is a const variable, which means it can’t change after compile time. But since I’m the lead toolset developer, I just commented out that rule for this mod, and everything seems to work fine 🙂

 

Making it convincing

A few areas of fast paced motion made it hard to have a convincing death unless I slowed the game to less than 15% game speed. This was sometimes jarring – so I combined it with the ragdolling feature where the player goes limp, to simulate the player dying. If you’ve ever watched me play these games, I love ragdolling and will call ‘testragdoll self’ at random points of the game to amuse myself – which, conveniently, taught me that some parts of cutscenes let you ragdoll without breaking the whole thing!

In a similar vein to using the slomo command, I also called this ragdoll command on AnimSequences that supported it. If it doesn’t support it, you simply disappear from the scene for some reason, and I’m not really sure why. It definitely improved some scenes, such as the last one shown in the trailer for the mod.

The logic for slomo and ragdolling.

Lost in time

Changing the game speed introduced additional complex issues that never show up in the vanilla game. One of the significant issues I had to solve was that some interfaces would run at the slow motion speed – such as the load screen UI and the game over UI. UI interfaces don’t interpolate the frames, so it felt like the game was super laggy. A couple of days of research into how the UI worked turned up a solution – there is a property on UIs that allow you to change the timing of them to real time rather than game time. This unfortunately added yet another side effect of the menu feeling very fast compared to other interfaces. So I could have the UI behave laggy (shown below), or have it run super fast to the point where it feels like the animation is off.

An alternative solution I tested was setting the game speed back to the original 100% speed when the game over timer expired – only to learn that the speed value only propagates on the next tick when done via console command – but when the game pauses, it won’t hit the next tick. Additionally, Kismet traversal only seems to occur on a tick, which means once the game pauses, I could not use Kismet to do anything anymore.

So what was my solution?

I combined both solutions – the game over UI has 3 simple buttons that fade in and out over a couple of frames when you mouse over them. I changed this interface to use real time, which means it runs a bit faster than the standard 30 frames per second it’s meant for – but I doubt anyone would actually notice this if I didn’t detail it here. As for the load screen, I modified the class in the basegame’s files to forcibly set the game’s time dilation value to 1.0 right before the interface is shown, instead of using the slomo command. The end result is how the UI normally looks:

This made the UI run at the correct speed, and did not affect anything else, as exiting the load screen at this point would take you back to the main menu. It also has the added benefit of not affecting normal deaths or the main load screen from the main menu. I don’t like to modify base game files if possible because they are more difficult to restore than DLC mods.

This could not be implemented into the load menu itself, as you might be in a section with time dilation, pause, enter the load menu, and then exit – so the solution had to be tied to the gameover screen, unless I wanted to further have to modify basegame files.

 

Too much optimization

A curious issue that came up was that loading a game save would produce inconsistent game behavior – sometimes you’d reload a save and it’d already be running in slow motion, but other times it wouldn’t.  After some trial and error, I determined that using the ‘Resume’ feature from the game over screen would result in this behavior. The resume feature never brings up the load menu, so the fix described above would not apply.

After tracing how the resume code path is different from the load menu to load save, I found that there is a ‘FastResume’ feature in the executable that seems to recycle the TheWorld object when reloading a save, I assume to improve resume speed. I could not find a way around this issue, so I simply made another edit to the basegame to disable this optimization.

The code for the original version of ResumeGame as called from the Game Over screen.

I didn’t notice any actual performance change in resuming, so I assume this was probably for the disc based Xbox 360 and PS3 platforms, and not really relevant these days (and yes, I do play on HDD, not SSD. I did not test on a MicroSD card though). I wonder what other objects are recycled here, and could we use them to pass data through a resume?

 

Not in my FaceFX

One final small issue that complicated things was Mass Effect’s heavy use of FaceFX, a technology used to animate a face to match a voice and display other emotions. FaceFX appears to entirely ignore the game speed (it seems to run on real time, which means game lag also affects it), meaning it will continue to play at normal speed, even if the rest of the AnimSequence is playing at a different speed. This means you might be laying face down on the ground but moving your eyes around and talking. Thankfully I only had this issue in the custom ending, where I faded the screen to black right before the face began to animate.

When the animation and FaceFX fall out of sync you can get gems like this.

Putting it all together

With the logic of how the mod works laid out, the pieces all come together with my ReducedPlotArmorBuilder pipeline, which has its source code available here (will be available soon!).

The layout of the development environment was kept clean and simple; all development assets was placed under a ‘Source’ directory.

Source layout for Reduce Plot Armor.

Under this was several folders that the builder would access:

  • Basefiles: The files to modify and apply edits to, based on the LE3 Community Patch 1.6.2
  • Helpers: Contains packages that contain my custom classes as well as the RPADeathRoll sequence
  • Images: Not used by pipeline, but stored images used in the mod
  • Launch: Not used by pipeline, but stored items for the mod launch
  • Patches: Stored files from other mods that I would need to patch, similar to Basefiles
  • Static: Files in this folder are copied as-is to the mod’s DLC directory. It contains files I premade, such as the image packages for the mod
  • TLK: Contains blank TalkTable files, which I loaded into a map of Localization -> Table, so I could update each with proper strings for that language
The RPA pipeline running. Exciting stuff, I know.

The high level overview of the pipeline is pretty simple:

  1. The entrypoint of the program defines the input and output directories. This allowed me to set the game directory itself as the output, allowing me to skip the installation step during development.
  2. The RPABuild system deletes all files in the destination folder and copies all Static files to the destination directory.
  3. RPABuild opens the Helpers folder and inventories custom classes in the packages. It also gets a reference to the premade RPADeathRoll sequence that will be cloned into the files. Technically the custom classes don’t need inventoried as they are already in the package that contains the sequence.
  4. The TLK files in the TLK folder are loaded and mapped to by their localization code.
  5. A list of fixed TLK strings are installed, for things that need a fixed TLK number assigned, such as text in the settings menu that will not change over time.
  6. The list of edits is pulled from my RPAList class and applied one by one to the game, which I’ll go over in a bit more detail below.
  7. TLK files are saved to the destination directory now that all edits have been applied to them.

Applying an RPAEdit object

The actual meat of the pipeline is only about 80 lines of code long, in a method called ApplyEditInternal. This method takes an RPAEdit object, the source package path and the destination package path. It opens the source package, clones in the RPADeathRoll sequence to the appropriate location, then creates the input objects as defined in the RPAEdit object, and connects them as inputs to the RPADeathRoll sequence.

Code to create the input variables of the RPADeathRoll sequence. It’s actually pretty simple.

A delay object is inserted (if necessary) into the sequence with the appropriate timing, which connects to the death roll. A branch is then added from an already existing object in the sequence to our delay or sequence, which is what triggers our sequence to actually run.

Finally, some AnimSequences are set to be unskippable – to prevent players from skipping over the death chance. It’s an unfortunate side effect of this kind of mod. The package is then saved.

For patches against other mods, this same system is applied, just with input files from those mods, and output into a directory that my mod uses to store patch files for other mods.

The final product

Packaging it all up into a usable mod also led to fixing several bugs in our tooling, such as incorrect inheritance detection of custom-made subclasses. While this mod definitely didn’t move tooling forward like it did with Pinnacle Station, I think this mod is a good example of pipeline-style mod development, and will hopefully serve as a good example for other developers who do this kind of work.

This was a fun little experiment.

There are several things I didn’t talk about here – such as learning how to use the Mod Settings Menu, a feature of the LE3 Community Patch which gives you an interface to change mod options while the game itself is running. I also didn’t cover how I used Wwise to generate a new audio bank and import it into the game, which is something we’re still working on the tooling for. I didn’t even mention the fun time I had trying to get ambient music to stop playing in the custom ending, and how I had to hackjob a way around it!

Future work

There are a few things that I may (or may not) get to in a future version of this mod, including making the blood screen effect work on all cameras – currently I’m not sure why it doesn’t affect certain cameras. I’d also like to see why ragdoll works in some AnimSequences but not others, so I can turn it on for more of them, if possible. Extending this to the other two games in Legendary Edition would probably be pretty easy, but the over the top cinematics are less common in earlier entries of the series. I’d like to improve the death roll result text to not have a spinner.

Anyways, thanks for reading!

On November 7th, 2021, the ME3Tweaks group released Pinnacle Station for Mass Effect Legendary Edition. Pinnacle Station was a DLC developed by Demiurge for the original Mass Effect game in 2009. Demiurge also handled the PC port of Mass Effect. Sometime after the DLC was released, the source code was lost, which is why it did not appear in the PS3 version of Mass Effect Trilogy, and was subsequently not included in Mass Effect Legendary Edition.

In this blog post, I’d like to go over some of the technical challenges and how the ME3Tweaks group ported Pinnacle Station from Mass Effect to its Legendary Edition counterpart, as well as detail a few of the changes we made. For clarity in this post, when I say ‘ME1’, I mean Mass Effect from 2008, and when I say ‘LE1’, I mean Mass Effect (Legendary Edition) from 2021. These are how the games are referred to in our modding scene to help avoid confusion.

 

Pinnacle Station

First, let’s start with some context. Pinnacle Station is a combat-focused DLC where Shepard must top the scoreboard on 4 different maps across 4 different game modes, though only 3 maps are available per mode. There is also a special map after completing all of the other scenarios, upon which completing you get access to “Shepard’s Apartment”, a small building on a remote planet.

Pinnacle Station had a great vista on the station, which in my opinion is one of its saving graces.

It is somewhat neat prize, but the DLC does not offer any story of any actual substance and nothing is referenced from this DLC in future games (as far as I am aware). I’m pretty sure this is often considered one of the worst DLCs in the franchise.

 

Origins and initial feasibility

This project started on September 18th, 2021, after I came back from a complete modding hiatus after working pretty much nonstop on modding from March until the end of July.

After seeing someone comment about wanting Pinnacle Station in Legendary Edition, I began to look into seeing just how capable our tools would be at attempting something like this. Our tools could already port assets from ME1 to ME3, and the LE games are based mostly on the ME3 version of the engine (not entirely, but data formats are mostly the same).

Terrain (the rainbow in the background) turned out to be one of the worst things to deal with in this port. What a nightmare. This is one of the earliest pictures, when we were still testing feasibility of the project.

Initially it was only going to be Shepard’s Apartment, which is the prize for completing Pinnacle Station. This would be fairly easy since there is not much sequencing involved, and only a handful of meshes, something that could be done in a mod without too much work. Over time the project evolved into the entire DLC, with a focus on making it feel more like part of Mass Effect Legendary Edition, rather than feeling like a port of content directly from Mass Effect.

The starting question for this project was simple, yet complicated: Is porting the entire DLC actually possible? What are the things we absolutely can’t port? Do we even know what they are?

Material Shaders

For years we’ve known we cannot port material shaders across games, even between LE games, because of some unknown data that trails the embedded shader files in the shader caches. I’m no expert on shaders, but shaders from my understanding are essentially how the graphics card draws surfaces of polygonal models. If you don’t have a shader, the graphics card can’t draw the polygon. Materials in Unreal Engine reference a shader by a GUID and provide inputs to it, such as color, textures, and other items. If the loaded shader caches don’t have this ID, the game instead uses the default material, which is the checkerboard pattern.

Modders can work around this limitation by finding other materials that provide a similar effect, cloning them, and providing different inputs to them. Not all parameters of shaders can be changed; some are meant to be widely changed (like tintable armor materials), others have no configuration options at all (like some VFX shaders that don’t even use textures).

We could never have ported shaders though, as Mass Effect and Mass Effect Legendary Edition run on different versions of DirectX.

SHADERS: Couldn’t be ported

 

Audio

Audio was the big question mark for this project. When starting this project, most of ME1’s audio system was not understood. We knew both ME1 and LE1 used ISACT, but we weren’t sure if the versions changed – though it was unlikely as ISACT has been dead since 2007-ish. If audio could not be ported, there was almost no reason to try to port the main part of the station, where dialogue is part of the story. Additionally, we currently cannot replace or add new audio to either ME1 or LE1 due to several unknowns in the audio pipeline for the games.

While porting the apartment, we found that audio would work if one just took the original ISB (ISACT Sound Bank) files from ME1 and put them into LE1, and porting over the objects that referenced it. This worked for all audio in the DLC.

AUDIO: Could be ported

 

Lighting

How will the maps be lit? Porting prebaked lighting across may or may not work. For a few years we’ve had a workaround for adding or moving new objects in levels: turning on dynamic lighting. This means the game could compute how objects should be lit. This is done all the time in the game already, moving objects for example use dynamic lighting to cast shadows and to shade them when they move into less lit areas.

SirCxyrtyx a few years ago wrote an experiment to turn all lighting on a level to dynamic, which yielded both terrible lighting (compared to the prebaked lighting) and terrible performance. But it was possible, and could possibly have acceptable performance on smaller, less complex levels.

LIGHTING: Maybe could be ported

 

Differences between games

We were not sure how much under the hood had actually changed from ME1 to LE1. Did things move around in game files? Were some things cut? Were some things added that needed to be used?

How an instanced full path is defined
How an instanced full path is defined. We found that a few objects in ME1 and LE1 don’t follow the rule that unique objects must have unique paths, which broke a lot of things and required manual fixes.

As part of the discovery phase, I built a system to generate an object database, which maps every unique game object to the list of files that contained them. Game objects in this context mean things like meshes, textures, particle systems, and more. This database differentiated objects by their full instanced path, as shown below.

This database would be used to build our donor system, which would be checked every time an object would be porting over from ME1 to LE1. The instanced full path would be used to find an identical asset in LE1, and if one existed, it would port that instead. This would mean we would extensively be using existing LE1 assets, such as shaders, updated meshes, textures, etc. Without this system it would not be possible to do this porting as we would have to manually point to LE1 only assets, such as Materials (which reference the shaders).

Using this system, we were able to determine that there is not a lot of internal differences between the original and the Legendary Edition version of the game, which meant porting levels would not require an enormous restructuring of the DLC. Some things would need changed, but nothing insurmountable. We also learned that Pinnacle Station has very few unique art assets, which is something it shared with Bring Down The Sky.

DIFFERENCES BETWEEN GAMES: There were minor differences that could be easily worked around, hopefully

 

Using VTest to port the DLC

Our porting process uses a workflow that became known as VTest, which means ‘Vegas Test’, as the original code was meant just to be a test. Vegas was the code name for Pinnacle Station during it’s original development. We used Vegas in the codename for our project, CrossGen-V.

VTest is a porting pipeline that takes a slightly modified set of files from the original DLC, runs them through a converter, and outputs converted files. As development progressed, we built in ways to improve development time, such as automatically telling Mod Manager to install the mod and run the game at the end of a VTest session. At the end of initial development (November 6), VTest takes about 6 minutes to run and uses about 7GB of memory.

The asset development environment for VTest. It’s also the root of the Mod Manager mod, but the version that shipped doesn’t include all of these folders.

 

The set of ME1 files to be ported are known as ModdedSource files. We made minor changes to these files , such as renaming things that need to be memory unique, or to take a specific donor object when porting. We also made changes that only have to occur in a single file here, like basic sequence fixes. These files would almost certainly crash ME1 if you tried to put them back into ME1, as they are specifically tailored for use with VTest.

 

The VTest pipeline is quite complicated and technical, but it can be broken down into a few main stages. Glossing over a lot of the very technical bits, the pipeline looks something like this:

  1. The destination mod folder (located in the Mod Library) is cleaned out, and files we never have to process are copied in, such as 2DAs and TLKs. These are known as PrecomputedFiles. These are files that don’t need conversion at all so they are simply copied into the mod folder.
  2. The ModdedSource files are run through the VTest conversion code one at a time. The ME1 file is loaded into memory, and we make ‘PreCorrections’ to this file, such as renaming objects so the donor system will use different objects. This also is done to make changes to the files so certain unnecessary assets are not ported over, but we don’t have to modify the original file (in case one of our edits breaks things).
  3. Each ME1 file is then run through the VTest converter, which does the actual conversion. It behaves differently based on which type of file is being converted:
    • Level files are run through a level porter, which starts by generating a blank LE1 level file. It then ports the ME1 level actors over, as well as all of their references. The donor system pulls in the LE1 versions of assets automatically, or our own specially crafted donors, if one is available.
    • Localization files generate a blank LE1 package file, and then port over the ObjectReferencer, which is used to tell the engine to keep the listed objects in memory. Because of these references, everything in the file is ported over by our tools, using the same donor system, except it forces use of the same language localization.
  4. The converted files, now in LE1 packages, are run through our PostPortingCorrections code, which is where the meat of the conversion takes place. It does things like convert properties that changed between the games (such as NavReferences), applies some fixes and applies specific changes we wrote, like fixing sequencing (such as installing BlockForTextureStreaming, which is not available in ME1) and the intensity of lighting.
  5. The package is then saved to disk in the Mod Library folder, and we run some checks on it. These include things such as checking the actor count in the level is at least 2, testing import resolution, ensuring correct texture NeverStream flags, and checking all property references that their type matches. We also run our standard toolset checks such as making sure entry and name references are within the bounds of their respective tables.
  6. As VTest completes, it signals ME3Tweaks Mod Manager that it should install a mod by passing it the moddesc.ini path. Mod Manager loads the mod and installs it. VTest also tells the Mod Manager to automatically boot the game afterwards.
VTest in action
VTest in action. It runs within Legendary Explorer as it depended on other experimental code. We will probably split it out of the toolset since it doesn’t really belong there.

You can view the source code for our pipeline on the Legendary Explorer GitHub .

VTest allowed us to make changes that would affect multiple files without having to do everything by hand in each file. If we needed to rescind a change, we simply commented out that part of the porting code and recompiled the mod with VTest. This ensured accurate results every time and allowed us to configure options, such as dynamic lighting, porting level models, flipping off certain levels to improve compile speeds, and more. It had the downside of typically having more up-front development time for changes, but the payoff over time far exceeded the upfront investment.

This means that manual changes after VTest were only done for testing purposes. We would install the mod, change files in the game directory, and when we acheived our desired results, we would apply those changes to ModdedSource or code them into VTest.

The result of taking Legendary Edition assets is that some things will look different, as many texture and material assets were changed. Most of these are not really an issue, but if you compare the two, you’ll see differences.

Specially crafted donors

During the porting process, we found that there are assets that are only existed in Pinnacle Station, such the simulator loading effect, the capture ring effect, and terrains that are used on maps like Volcano.

We made special donor files for these, which are inventoried at the start of a VTest session. The object database is updated in memory, allowing VTest to think LE1 assets exist, when they’re actually only part of our special donors. For example, we remade an object with the same exact instanced full path as the planet you can see outside of the window aboard Pinnacle Station; when this object was being ported from ME1, it would use our custom object instead of trying to port the ME1 version, which would not work in LE1.

We built a system to quickly turn an existing object into a donor file, which housed the single object. We could then customize the object in that file, and every time VTest ran, it would use the object from that file. This essentially allowed us to redirect objects during porting.

Tracking progress

We used an excel sheet to track issues and how they were fixed, as well as other things such as donor materials. This project was not big enough to warrant using a larger system such as JIRA or GitHub issues. We would fill this out as we went along. As of initial release, most of them have been checked off, with a few being most to post-release.

The tasklist we had of things to do, and their severity. It always seemed like the for every finished item, two unfinished items would be added.

Reusability of VTest

I’m sure some modders will read this, and see what everyone wants to see: A convenient, easy way to port content (like their own mods) from the original game over to the legendary edition of it. Unfortunately, it is unlikely this system would ever work in a generic manner that mod developers hope would work.

VTest is several thousand lines of code that is very heavily tailored to porting Pinnacle Station. The donor system however is fairly generic, but only properly works if the source files are properly formatted – same named objects MUST be identical, which for mod files is rarely the case.

For this project, we tried as best we could to adhere to the official compiler rules of every object being the same, including its references. While we didn’t hit it 100% (due to the large amounts of work it would have involved in one or two situations), we wanted to make this release feel as official as possible, even if users would never feel the impact of these changes.

 

Design issues

This DLC had a lot of design issues that we put significant effort into fixing, in order to make it feel a bit more modern.

Lack of audio

In the original Pinnacle Station, simulator maps had no music, and most enemies had no audio (grunts, barks, etc). It was pretty boring to hear your gun shoot for minutes while Ahern yells at you the whole time. We decided to add music to each map to make it more entertaining to play, which had a surprisingly profound effect on how enjoyable each map was.

We don’t know much about ME1 or LE1’s audio system, ISACT, so editing audio is impossible. We know most of the information required to change audio, but there is unknown data in an object named BioSoundNodeWaveStreamingData, which seems to be required to make things work. So we had to limit ourselves to the original versions of audio in the game.

We tried to pick tracks that felt appropriate for the map theme. All maps except Warehouse have a second track that is switched over to as the combat intensity picks up, and it makes a world of a difference in how fun the scenario feels. It’s one of the best new features in my opinion.

We designed sequences that would trigger on specific values per map and game mode. In this instance, when there are 10 enemies remaining in Time Attack, the music intensifies.

Kill volumes

Kill volumes were a significant problem when porting this DLC. In the original DLC, the maps had a bunch of kill volumes just outside the playable area, which was so enemies don’t fall out of the map and survive.

These kill volumes did not discriminate who touched it, and would immediately destroy any gameplay pawn that touched them. On top of looking pretty jank when you punched someone into a railing and they just disappeared immediately, it also had a pretty unfortunate side effect of also deleting Shepard and squadmates too. If Shepard is deleted, the game crashes shortly after. This could happen if you were ragdolled into it, or tried to use console commands to move through the air, such as ‘fly’.

There are invisible ledge meshes placed into each map that are designed to prevent the player from being able to walk off the ledge into these volumes, but due to what were essentially rounding errors when our tools converted these meshes from ME1 to LE1’s format, there were gaps in coverage (due to changes in kDOP). This would constantly lead us to game crashes as we would walk into what we thought would be solid, but instead wasn’t. This was one of the first things we had to fix.

If Shepard were to walk to the right a bit more, she would clip through the collision into the kill volume. The game would crash a few seconds later. We had to use the Unreal Development Kit to regenerate the collision data correctly.

In our remastered version of Pinnacle Station, these kill volumes no longer do anything to Shepard, in the event you somehow are able to touch them. Additionally, we changed the behavior to cause killing damage to other creatures, rather than destroying the pawn, which means:

  • Enemies that touch it will die and have the normal death fade
  • Your squadmates will unfortunately be having a permanent nap for the remainder of the scenario if they touch it, as they will be killed as soon as they are revived

The second point we may address in a post-release update, however, we have not been seeing squadmates touching these volumes unless you use the in-game console and get out of bounds, which cause your squadmates to teleport into a kill volume.

Sorry Wrex. This tower’s not big enough for both of us, apparently.

Difficulty scaling

The original Pinnacle Station had significant difficulty issues on both the Casual and Insanity ends of the spectrum, depending on the game mode.

 

Volcano Hunt Insanity

This scenario was extremely difficult because of the map design. Spawn points were so spread out that getting to enemies often takes significant amounts of time, which you don’t have a lot of in this game mode. Coupled with the time to kill on this difficulty, you had to nearly be perfect.

We’ve toned this specific scenario’s difficulty down a little bit by reducing the decay of time awarded per kill, which should take a bit of pressure off. There is still significant difficulty, but we don’t want it to feel impossible. This only applies to Volcano Hunt Insanity.

 

Survival engagement

Survival mode has seen significant changes in our version of Pinnacle Station that results in increased difficulty. Survival mode is based on waves – after you kill all enemies from a set of spawnpoints, it goes to the next wave, which has harder enemies. As the scenario progresses, it gets more and more difficult, and eventually you are overwhelmed and the scenario ends.

Except, if you don’t kill enemies, the waves don’t progress very fast. If an enemy takes no damage for a while, the game kills them and spawns a new one (thinking they might be stuck somewhere). By simply not engaging with enemies, you can make harder enemies take minutes to spawn, which results in an easy win. This made it super easy to cheese, especially with ME1’s poor AI. This is definitely not Mass Effect 3 multiplayer.

You can try to run and avoid enemies, but they’ll run and find you.

In our version, we’ve made it so that enemies who have not been killed after about 25 seconds will charge the player. This makes them rapidly close distance and stay within firing range of the player. It greatly increases the difficulty, which is likely what the original mode was intended to feel like. It is still fully winnable, but there’s a lot less standing around waiting to shoot.

We will likely refine this change in future updates.

 

Survival duration and Casual

If you play well enough, Survival can go on forever. Even with the new engagement changes, you can still be hard to kill, especially with rapid health regeneration. In fact, on Casual with Soldier Rank 6, you were practically impossible to bring to 0 health – which means you couldn’t finish the scenario without having to use the exit button, which counts as a loss!

We added a system to slowly decay the player’s healthgate the longer the match continues. The healthgate system in LE1 greatly reduces damage Shepard takes when their health would drop below 25%. Certain types of damage ignore this system, like rockets, but most weaponry is affected by this system. The result is that you could regenerate health faster than you would take it, and would constantly be stuck around 25% or more health.

This decay system doesn’t kick in until about a minute and a half into the match, to make sure you have a chance at topping the scoreboard. We don’t want to punish players, but we want the game to increase in difficulty over time. When the round finishes, your healthgate is restored back to normal. We even coded it so that if a mod changes the default healthgate, our system will respect that value.

This change allowed players to be killed on Casual (so they could win the scenario), and also made longer sessions get more difficult. Who would have guessed a game being too easy would make it impossible?

This change is applied on all difficulties, but only on Survival mode.

 

Smoothing out the jank

I do not think it is a surprise to anyone who has played the original Mass Effect game that there are lots of ‘jank’ moments – like modal LOADING dialog boxes, textures pop-in, and audio cutting off at times. These problems are everywhere in the original Pinnacle Station. We spent considerable effort trying to minimize them, to make for a better feel to the DLC.

  • We preload files that we know will appear ahead of time to minimize the amount of LOADING dialogs that show up. If you mash your way through things like the scoreboard, the files may not load in time and you’ll still get them, but for the most part they should be gone.
  • Texture pop-in should be greatly reduced. This is a very common problem in ME1. We identified moments where we could load in textures that we knew were about to be shown on screen, as well as making use of black screens and blocking for texture load to force the game to wait until textures have streamed in.
  • We fixed poor lighting in a few areas that seemed like they were missing lights, like when Ahern gives you his special mission.
  • We fixed strange things such as replies on the conversation wheel changing positions depending on which conversation option you last chose.

We fixed a bunch of other small issues, some of which are shown below:

Comparison of our version of Pinnacle Station vs Original Trilogy
This turian in Ocaren’s room had the wrong material assigned to him, as it was for a different armor. The result is the mess you see on the right. We fixed it.
Kinkojiro put a lot of effort into fixing the geth flashlight position, which was broken in the original due to how the enemy is spawned.
In the original game, the turian faceplate did not get the holo effect. HenBagle spent a lot of time getting it to be applied, which makes it much better looking when an enemy spawns. Due to the faceplate also being holographic, it is a bit hard to see in the left picture.
Demiurge made a ledge collision mesh for the Subterranean map that prevents you from walking off edges, but they never installed it into the map. We did, which prevents a few holes you could walk into and never get out of. This map had tons of other bad collision issues we had to fix.

Bonus content

During the development of Pinnacle Station for Legendary Edition, we had to comb over just about every part of the DLC. There was some interesting discoveries, including an easter egg and quite a bit of cut content (that is for the most part not usable).

Ahern’s easter egg

There are 3 voice lines for Admiral Ahern that he says when you shoot the viewport window while in the simulator. This window is in the top corner of every map. It has a 1 in 20 chance of triggering a voice line, so it takes several shots to trigger one of three random lines.

Given that you are timed in most missions, they start immediately, and on some maps it’s hard to even see this window, and that it takes multiple bullets to trigger the line typically – it is possible that nobody has ever found this easter egg. One of the lines Ahern delivers is one of my new favorite lines of his.

Shepard shoots the window in the tropical map
I can’t imagine how anyone would have found this in normal gameplay. You can’t even shoot it by accident due to how high the angle is and the lack of sightlines to it on most maps.

Cut content

This DLC seems like it was much larger in scope during development and had to have almost all of its story cut for unknown reasons. There are some non-finished conversations with Vidinos where he badmouths you, which we may still implement as an an ambient voice line in a future update. There are a lot of interesting small things, such as how Bryant was at one point seemingly in the role of Ahern, before he got demoted to having just a couple of voice lines.

Figure showing waterfall of cut lines
All of those nodes on the right are cut lines for Bryant. Many of them are similar to Ahern’s lines. We can only see the first couple of words for each line, there are no localized strings or audio that exist for these.

One item that was restored was the turian bombers on Ahern’s map. Every 30 seconds or so turian ships will fly in and bombard the battlefield. I imagine this was cut due to the lack of warning when it occurs, but they don’t do much damage and create something interesting on his rather mundane mission you have to do.

These bombers would have flown over really quick and explosions would appear on the field. The one issue is that they have no audio feedback, the field just kind of explodes if you aren’t looking at the sky. We put them into Ahern’s map and will try to improve them in later updates.

Issues with our version of Pinnacle Station

While we put a lot of effort into making this DLC an improvement over the original, without actual tools and source code, there is no way we could make everything better, or even equal. If we had the tools do this, we would love to properly remaster this DLC.

Things that would have helped would be an in-game shader compiler (that outputs a local shader cache file, like ME1 did), the remaining piece of information we need to edit ME1 audio (how to generate BioSoundNodeWaveStreamingData objects), and a way to compute pre-baked lighting. Being able to generate cover data would also have helped a lot, but for this project we improvised.

Performance

Due to having to dynamically light nearly everything, performance suffers pretty heavily on some maps when the game is played using older graphics cards. Unfortunately, without the actual unreal development tools, we have no way to compute prebaked lighting, which would solve pretty much all of the performance issues. As a result, users with graphics cards lower than a RTX2070 or AMD RX6700 will likely have some framerate issues if you try to go above 60fps.

Shows the original lighting when applied to the file in LE
This is what it looks like if you try to take the original lighting. It looks terrible because the lighting was computed for different, lower poly meshes. We had to throw almost all of it out.

We are working to improve performance by seeing if we can convert more lightmaps, but this will take engineering time that simply is not available before launch. The type of work we are doing with lighting is not something that has been done at this great of a scale in our modding scene.

While the framerates may not be the best in the launch version, we did put some significant work into improving performance:

  • We cut dynamic shadows, as they are not necessary on almost all objects. We may selectively turn some back on where they would be noticable. This only affects objects that cannot move, such as level geometry.
  • We ported lightmaps from ME1 for meshes that had the same amount of vertices. For the most part, these look correct, but a few stand out as not being 100% accurate.
  • We took the ME1 version of a few assets, such as stones and rocks, that make up large chunks of geometry on some maps. These objects we found don’t need to have as many triangles to still achieve an indistinguishable look, but using the ME1 mesh asset reduced the amount of vertices needing shading by tens of thousands on some maps. It is unlikely players would even notice which assets we did this on. Post launch we will likely look at doing this for more things on the more problematic maps.

In developer versions of this DLC, I had a NVidia RTX 3080 that could not even reach 70fps at 1080p. With optimizations we could reach 1440p 120hz pretty reliably on most maps.

Cover system

I will admit, I haven’t actually really played any of the Legendary Edition games. To me they are pretty much the same games I’ve modded for nearly a decade, which I’ve also only played through like once or twice (though I did play like 4000 hours of multiplayer, almost all of which was modded). That said, I still really like these games, and the challenges of modding them – finding creative solutions to problems when you have a mostly rigid set of constraints.

The cover system is complicated, and the ME1 system was missing data that needed filled out in LE1. We just guessed what these values might be since we had no idea how to compute the values.

I’m not sure if the enemy AI improved in LE1, but they still seem as intelligent as a can of beans, which is about how I remember them from ME1. There are some oddities in how the AI pokes out of cover in our ported cover, but I don’t think this is too much of an issue besides being cosmetic. The AI could be more intelligent if we had a way to compute the dangerousness of a cover slot (a place where AI can take cover) when compared to another cover slot on the map.

Localizations

In the initial release of Pinnacle Station for Legendary Edition, we are only supporting the English voice audio. There is full text localization for German, French, Italian, and Spanish. We will update the release with audio support for other languages in the future, however there was not time to implement updates to VTest to account for localizations, as it’s quite complicated, and we had to do a ton of work to get English dialogue not getting constantly cut off.

Not porting assets correctly when accounting for localizations means you end up with a scoreboard in another language, or Ahern speaking french to you in a German language game. We also need to find some localizers to translate the DLC’s text into Russian, Polish, and Japanese, as those languages don’t appear to have been supported in the original version.

Conclusions

This has been the most ambitious modding project I’ve taken on before, as it touched pretty much every aspect of design. We had to design multiple new custom classes, code a very complex porting pipeline, improve our tools to find assets easier, invent new debugging tools, and more.

I didn’t cover everything in this post, like how we had to adjust the length of conversation matinee’s due to some strange bugs, or how we couldn’t get BioActorFactory to work at all for some reason, which is why the ‘bitsplosion’ effect on enemy death is missing. But I hope this was an informative behind the scenes look at how this was DLC was ported and improved.

We plan on updating the mod to further polish it, as well add some quality of life things, such as a weapon store on the station and adding in a bit of the cut content where it makes sense.

This was our most ambitious project yet. We hope you like it!

We hope you’ll try it out Pinnacle Station for Mass Effect Legendary Edition, and see what some extra polish can do for a DLC that originally didn’t seem to get much!

 

 

 

 

 

 

DLC modding has been a valuable system for modding the Mass Effect games for the past several years. It allows mod developers to create a self-contained mod that can be easily added and removed from the game, all contained with a single DLC folder. While they are not the perfect solution for modding, they have been a great benefit to our scene.

 

For Legendary Edition, we’ve had support for DLC mods in LE2 and LE3 for a few weeks, and many mods have been created already that use this system. The same can’t be said for LE1, which until today did not have DLC mod support. There are several reasons this took longer to get shipped out the door, which I’d like to explain a bit. I’d also like to explain the limitations we have on DLC mods for LE1. The July 21st update (7.0.3, Build 121) of ME3Tweaks Mod Manager supports generating and installing LE1 DLC mods.

 

What composes a DLC mod?

DLC mods include a variety of content; strings (contained in TLKs), packages, movie files, config file updates (.ini, .bin), Table of Contents (TOC), conditionals, among other things. In the original trilogy, DLC modules were ‘mounted’ for use all at the same time at the main menu. The DLC would be parsed, overrides would take effect, and files that needed to be loaded into memory would immediately do so (as startup packages).

In Legendary Edition, this has changed slightly. DLC for LE2 is loaded and mounted before the splash screen even appears; in LE3 the DLC appears to be read but not mounted until the main menu has completed (e.g. loading a save). This has side effects, such as being able to put certain files into DLC  that would not work in the original trilogy. The flipside of this half-loaded state is files that load before full mount cannot use certain things like TLK from the same DLC they load from.

The mounting step of DLC registers the startup packages, loads the TLKs, merges the configuration changes, loads the 2DA tables, etc.

 

What’s different in LE1?

LE1 is based on the PS3 port of Mass Effect from 2011. Pinnacle Station was lost already at this point, so it seems Edge of Reality (the porting studio for the PS3 version) moved Bring Down the Sky into the main game directory (CookedPS3) and gutted most of the DLC system; the game no longer looks for AutoLoad.ini files, it uses a single hardcoded path. Additionally, some features such as DotU were also gutted, even though they still appear in the AutoLoad.ini for Bring Down The Sky. The DotU system told the engine to look for unrealscript classes in the specified files and load them into memory.

In the original game, there was no override system; ambiguous packages defaulted to the first package found, which was always the basegame one. The game used a primitive version of what would later become the TOC file, known as FileIndex.txt, which listed game package files. We worked around this by just making the game never see the basegame version if a DLC version existed; we invented our own DLC mount priority system. This was achieved through updating the FileIndex file to remove the basegame entries and use our DLC ones instead. We ported this over to LE1 to allow for overrides, but instead of FileIndex it now uses TOC.

With significant engineering effort from multiple parties, we also enabled DLC mount, by hijacking the AutoLoad.ini parsing function to also parse our own AutoLoad.ini files, so DLCs in LE1 can load things like 2DA, TLK, and startup packages through the ‘GlobalPackage1/2/3…’ system. Combining the TOC and AutoLoad workarounds, we have a mostly working DLC system.

One upside of the new implementation of TOC is that we can now override movies. In ME1, FileIndex did include movie files, and as such, the game always preferred the original file it found. With TOC, movie files are included, so we can override them now.

 

What’s the catch?

Sadly, since there was no DLC system for the PS3 port of ME1, there was no need to include the ini merge system, and it was stripped out. This means you can’t update items you see in Coalesced.bin for LE1 like you can in LE2 and LE3. It is possible to restore this feature, but it would likely require significantly more effort than was already put in for AutoLoad.ini support, and if it does come about, it will not be for several months.

This can be semi worked around by using the merge mod format in ME3Tweaks Mod Manager, and disabling the config flag of the property you are updating. Due to the complexity of stacking mods, you won’t be able to install new structs or array items, which is a known weakness I’m looking to find a solution for in the near future. The long term ideal solution would be to allow the changes to be merged from DLC mods.

 

Conclusion

The LE1 DLC system missing ini merge will be a potential issue for some mods, and right now there is not a 100% workaround for it without replacing the basegame coalesced file, which is widely incompatible with other mods. Hopefully this can be remedied, but for now, you can ship DLC mods for LE1.

Hello everyone!

 

It’s been a couple of months since my last post. Since then, Mass Effect Legendary Edition launched, and I just released ME3Tweaks Mod Manager 7.0 BETA, which supports Legendary Edition games.

 

As a quick summary, here’s what you need to know:

  • Mod Manager is in Beta and is not yet fully stable or localized
  • Legendary Explorer is still in development, and will be released soon
  • Multiple new mod formats have come about of Mod Manager 7 which will change how modding games is done
  • LE1 DLC mod support is being worked on

 

Mod Manager 7.0 Beta

Mod Manager 7.0 is the first release that supports Legendary Edition, and took about 5-6 weeks of pretty much full-time work to get up and running. It’s not fully stable yet and is having some bugs worked out but most of the features are stable and working. This includes things like creating starter kit mods, installing mods, etc. It should become stable pretty soon.

 

Coalesced mods for LE2/LE3 should be done through DLC mods, not basegame coalesced edits. DLC mounts before the title screen shows up.

 

Legendary explorer

Legendary Explorer is still in development, and is nearly done. There have been some recent unforeseen issues come up that we are having to work out with distribution and a few remaining things. This has been a full team effort as we have had to update a nearly decade old codebase to add 3 brand new games. We expect this to start being available for use soon – we have done some testing with veteran modders in read-only mode, and a few mods were developed and released by toolset developers as ways to stress test the toolset (and we found and fixed a ton of issues developing them).

 

New Mod Formats

As part of ME3Tweaks Mod Manager 7.0, I have developed several new mod formats that will ease issues in the long term scene that have plagued the Original Trilogy scene.

 

Merge Mods

Merge Mods are a new mod file format named m3m. These files are parsed and installed by Mod Manager and apply a specific set of changes to high traffic files like SFXGame.pcc and Startup. In the OT, some mods shipped these files (like ME2Controller), which means other mods that wanted to modify those same files had to make patches built on top of those files.

This made it exponentially worse for every new mod. Users could not choose which to install – it was one or the other for most mods. Merge mods, for the most part, fix this issue by only changing select parts of the file, and is a key part of the new scene. Many quality of life changes can only be done in these basegame files that cannot be shipped in DLC mods.

As such, it is important that changes to these files be distributed through the merge format, which will extend in scope over time. Shipping full file replacements for files breaks a lot of other mods and has caused long term harm to the OT scene, I have seen many mods simply be abandoned due to having to compete with well established mods. Over time I plan to expand what merge mods can do, but for launch I have limited it’s scope.

You can update properties, scripts, and merge assets into existing entries – asset merge is like doing drag/drop in ME3Explorer and replacing existing data (with relink).

 
Plot Manager Sync

Plot Manager Sync is like a special version of the merge goal, targeting ME1/ME2/LE1/LE2’s plot manager file. The Plot Manager file controls a huge swath of story related progression tracking and is used all the time in the game. Due to how Unreal loads things into memory, loading the basegame one first means values in it could not be overridden. This format changed in ME3 so this does not apply to ME3/LE3.

Plot Manager Sync will find specifically named files in DLC mods and apply their changes to the basegame plot manager sync. This will allow multiple mods to modify this file without overwriting the other. This feature will require updates to mods to work, but there are only 3 known mods that touch this file, and only 2 of them are available for download, with both devs updating their mod soon.

As of writing this post, this doesn’t work for LE1, as we don’t have proper DLC loading yet.

 
Squadmate Outfit Merge

Note: This does not work yet and will be enabled in a future update as of writing.

In ME2/ME3/LE2/LE3, what outfits a squadmate can wear are determined by what’s listed in the BioP_Global.pcc file. As such, this has the same issue of competing for files if you want to add new outfits that don’t replace existing ones.

Squadmate outfit merge allows Mod Manager to automatically build a BioP_Global.pcc file, along with related data (conditionals, etc) to allow you to add new outfits for squadmates, and have them all work nicely together. By adding a specifically named file in your DLC mod, it tells Mod Manager which files are part of an outfit. All information is then built into a new merge DLC mod that M3 makes, and your outfit works.

This should allow wardrobe mods for squadmates to co-exist.

 

LE1 DLC Mods are being worked on

Unfortunately, the DLC loader that ME1 had doesn’t exist in LE1. It seems it was crippled by the PS3 port, which is what LE1 is based on. We currently have package overrides working, but DLC mount doesn’t work yet (Bio2DA overrides, TLK, etc). We have a plan to get it to work but it’s going to take some time to implement and test.

It’s going to be more difficult to support ini merges however, for things like bioengine.ini, etc, as the ini merge code also appears to have been stripped out. This will significantly complicate some mods, but a few mods have worked around this, such as Better Camera, which is a merge mod (so it’s widely compatible) and doesn’t touch Coalesced, which does not have a merge feature (text file merging is VERY complicated and error prone, and is often not able to be automated without manual intervention – which users won’t understand).

 

Conclusion

Glad to have some of these tools out for use, I have seen some interesting and fun mods already being made. One thing we have noticed is some very toxic users of some mods who seem to think they are entitled to the work that we do.

We have spent literally 5 straight weeks working on this, for users to use, FOR FREE. Being toxic or abusive helps nobody. People make mods on their own time, out of their own passion, and many of the recently released ones are designed to be widely compatible. They don’t owe you anything – how much work did you put into these mods you’re installing?

It is very demoralizing to have such toxic mod users who think of nothing but themselves. People who make mods are humans too. If you have nothing constructive to say, please don’t bother.

 

That’s all for this update. Until next time,

Mgamerz

 

 

Mass Effect Legendary Edition arrives in less than a week, and I know many of the mod developers in my scene are excited to start working on it. With that said, I am writing this to ask you to have patience, as updating the tools we’ve spent more than half a decade building is going to take some time. All of these tools were built under the assumption that there would be no more than 3 games.

TOOL UPDATES

I’m here to outline what’s going to be occurring with each of the major tools in the scene.

ME3Explorer (UI ApplicatIon)

ME3Explorer is going to be put into maintenance mode. This means that we won’t have further updates beyond just some simple bugfixes – it will essentially remain as it is now, and will only work on the original 3 games. We’re hoping to issue one final stable build.

We have been working for the last couple of weeks to migrate the existing UI application’s code into a new project, named Legendary Explorer. This application will (eventually) support the Legendary Edition versions of the games, as well as also support the original versions. We are doing this for a couple of reasons:

  1. This lets us do a refactor of the project, as the app has had some pretty messy project structure for years now
  2. It will better convey that the tool works for the Legendary Edition of the games
  3. We are upgrading the project to .NET 5, which is a pretty significant change from .NET Framework 4.8 (do not let the numbers confuse you, this is many years of change). This will require some significant changes to how we also deliver stable and nightly builds to you, and it will take some time before we can get back to automated builds for users. Using nighties will require installing the .NET 5 runtime

The latest nightlies of ME3Explorer have exe version checks, which will prevent adding LE versions of games for game paths, in the event they use the same names of executables as the current games.

ME3ExlorerCore (Library)

ME3ExplorerCore (ME3C) is going to be put into maintenance mode. A new library, named LegendaryExplorerCore (LEC) (or just LegendaryCore, we haven’t decided), will essentially be a copy of ME3C, but with support for Legendary Edition games as well. This is being done for the following reasons:

  1. Projects targeting ME3C will not be broken when we have large codebase changes. While this could be accomplished via an older branch, we wanted to make a whole new project to ensure consumers of this library don’t accidentally upgrade and break loads of stuff and waste their time. The original scope of ME3C did not include LE games, and a lot of code was written under that premise, which if we added LE support, would likely break a lot of stuff.
  2. If you want Legendary Edition support, you switch to the new library, which will have a slightly different (but mostly the same) API. This will force you to update your application to handle the new game IDs and behavior. Apps using LEC can still work on OT games.
  3. If a user of ME3C wants features from LEC, they can backport it to ME3C.

ME3TwEaks mod manager

I’m not 100% sure yet on the plans for M3. I’m thinking of making a ‘new’ Mod Manager named Legendary Mod Manager, but it would be just a rebrand of ME3Tweaks Mod Manager, with support for LE games as well. This will take some time – until we know what we’re dealing with in LE, it will be hard to make Mod Manager support, and I will be working on mod development tools to start with.

Essentially, M3 would be put on ‘maintenance’ mode for some time, before it upgrading it once to support an upgrade to Legendary Mod Manager, LMM, similar to the transition that occurred when moving from ME3CMM to M3.

This plan would be for the following reasons:

  1. Users don’t seem to understand that ME3Tweaks is my organization’s name. Not the name of the program. I have lots of other software that I release under my organization.
  2. It will take some time to figure things out for Legendary Explorer Core. M3 depends on ME3C, so until we have LEC up and running, LMM won’t really be able to depend on it. Things like opening packages, lots of helper directory classes, that sort of thing.
  3. Mod Manager is far more complex under the hood than many users think and adding three more games to the codebase, even if similar, will require a pretty huge investment of time.
  4. The server infrastructure that M3 currently uses pretty heavily will need some significant changes to accommodate more games in the databases, web pages, etc.
  5. I want to split many core features of M3 into its own library that can be used in other projects, like how the GameTarget class is reused in ALOTInstallerCore, MassEffect2Randomizer, etc. It is a lot of effort to support these duplicates, but it is also hard to make a localizable core library, especially for programs that don’t use localizations.

ALOT Installer

ALOT Installer has some more features planned for the Original Trilogy (OT), but I simply haven’t had time to work on it. ALOT Installer will remain OT only for now.

Once Mass Effect Modder is updated for Legendary Edition (this will likely take some time) I would like to make a general purpose texture installer program that works for LE. I do not expect it to be cross-compatible with OT. A general texture installer would be nice, especially with the same UI as one of the most popular OT mods, however texture modding will not be a high priority for me to start.

Mass Effect Randomizer / Mass Effect 2 Randomizer

These will remain working for their original games. At some point I would like to revisit them for Legendary Edition but it will likely be months if not years. Work on these projects have significantly benefited ME3Explorer (+ME3C), as they stress test the library and help identify weaknesses, and also lead to new features, such as memory-safe extraction, import resolution, and new debugging tools.

ME3TWEAKS MODMAKER

ME3Tweaks ModMaker will remain as it is and only be for the OT version of Mass Effect 3. While I am very proud of the work I did for ModMaker, it is an absolutely huge project that has extremely high support requirements, and was built when I was a much more amateur programmer. 

Since there is no multiplayer in LE, I don’t really see a need for something like ModMaker, which mostly was a gameplay modding tool. ModMaker worked well because multiplayer was very gameplay focused, so changes through ModMaker would be very noticeable, and could work in short spurts. This type of modding doesn’t really translate well to singleplayer which has a lot of things that are specific to each level and takes place over the course of the entire game, and also doesn’t work well due to how the game is compiled – singleplayer and multiplayer use very different strategies for asset loading.

It is a true shame there is no multiplayer in LE.

Gibbed SAve EDITORS

Gibbed has expressed interest in updating his save editors for LE. I don’t expect this will be too much work for ME2/ME3 since it doesn’t look like much has changed in those games. As I am not part of the development of these programs, I don’t have much more info on this.

Mod Guidance

I’m sure many developers want to be the first person to release a mod for LE. In the beginning there will be lots of teething, and there will likely be lots of buggy software. As someone who has to support mods through Mod Manager, there are some things that will be very helpful for later development in the scene:

  1. Do NOT use exe installers. These are an absolute nightmare to deal with in the scene, and if some new modding method comes along, nothing can really work with exe mods. Mod Manager can be updated to account for new processes or installation corrections. Manual installation directions can be adjusted. Exe installer based mods cannot have their install behavior changed.Exe installers are also opaque, so it’s hard to know what it will actually install. It makes identifying incompatibilities difficult, especially if only a specific configuration installs a file, but the others don’t.
  2. Do NOT leave mod pages with outdated main downloads paired with ‘updates’. These are a huge waste of end user’s time, and also waste tons of developer time because we end up having to support users who don’t know what they’re doing. Spend 20 minutes of your time integrating the update so you don’t waste collective thousands of hours of other people’s time.The exception to this is for mods like ALOT, which are huge, but have whole systems built for handling the update in an easy way.
  3. If the new games use the DLC system, DO NOT release variants of your mod with the same folder name. These are not possible to distinguish without tons of investment of my time. Many early mods in the ME3 scene did this, and the only thing it does is confuse users! What version of a mod did I install? Who knows? They’re all named DLC_CON_FUNMOD.Mods installed through Mod Manager (that are designed for Mod Manager) don’t have this problem as I tag the options selected, so even if you have 3 ‘variants’, users can see what was installed. 
  4. Use DLC folders if they are supported – basegame file replacements SUCK unless they are absolutely required. They are hard to track, require lots of manual inventorying by me to identify where they came from, and are a pain to uninstall for users.
  5. Be prepared for game updates. There will likely be a few months of them and they may break your mod. If files in your mod end up being updated by game updates, but your mod isn’t updated, I may blacklist the mod from installation in Mod Manager due to it leading to different ‘versions’ of games. All mods should be built from the same ‘base’ version of the game, which will eventually be the final game update, whenever that is.This may sound inconvenient to you, but it is much more inconvenient for us developers, when we have to code around poor decisions by mod developers. Make our work easier so we can focus on better tools rather than coding around your work. You may think this is a one time cost, but adding lots of edge cases just means every time we update something else we have to make sure it still works with edge case code, which has an ongoing burden.

 

There will be some significant pain at the start of modding these games.

  1. Some formats have likely changed and will take time to reverse engineer.
  2. There will be no reliable cross OT <-> LE modding for some time. Please note that when I say OT <-> LE, I mean porting individual pieces, like the properties of an object, not running a mod through a converter, this will simply not be possible due to them changing to DirectX 11.
  3. There will be a lot less debugging tools – we will not have an asi loader, we may not have console access for a bit even. Pray that they leave some debugging tools in the game for us, because it’s going to suck BIG TIME if we are left with nothing. Why do you think there are so few ME2 modders 😉

 

Again, please be patient. There will be a lot of teething issues for setting up modding in these games. Remember that developers are people too 🙂

Hello everyone,

I’m Mgamerz, owner of ME3Tweaks.com, and the lead developer of the Mass Effect Trilogy editor toolset, ME3Explorer. I also write end user installation tools ME3Tweaks Mod Manager and ALOT Installer. If you’ve modded the series recently as an end user or a developer there is a high likelyhood you’ve used my software to do some or all of it.

Recently, BioWare announced Mass Effect Legendary Edition, which is a remastered version of the original three games. I’d like to share with you what I know about the new games, what they mean for modding, and what my plans are going forward, as I have had many people ask or tell me things that need to be cleared up.

 

Will existing mods work on Legendary Edition?

No. To be clear, when I say ‘existing mods’, I mean package file based mods, which is the majority of mods. Anything that ends in .pcc, .upk, .u, .sfm are package files, and are what comprise of the majority of each game.

Our files are tied to certain engine versions of BioWare’s fork of Unreal Engine 3. On top of this, each game in the trilogy is on a different build of Unreal Engine 3 with more and more BioWare changes on top, and that spans 5 years of engine development by Epic Games as well. Unless BioWare changes absolutely nothing in their game, things will not work.

Complicating matters is that while our modding tools have improved greatly recently, many mods were built with older, buggier tools, and it is more a bug that they actually work in the game, rather than them actually being properly supported. Many of my older mods even have issues, but the game somehow still works with them.

Essentially, you can think of it this way: Will vanilla package files from the trilogy games work on legendary edition? I’m extremely doubtful, there are too many dependencies on core files that are nearly guaranteed to be changed.

 

But BioWare says they’re working with the modding community?

BioWare may be working with some people, but I’m not sure who. I’m curious, but I also understand whoever they are, they are under NDA, and asking people things who are under NDA just makes their life more difficult.

However, I’m not under NDA, so I can tell you what I know. Nobody in the toolset development group has been contacted, which consists of three people: Myself (Mgamerz), SirCxyrtyx, and Kinkojiro. We are the ones who build and maintain the tools (ME3Explorer) that are used to make mods, and I personally am the one who also builds the tools that installs the majority of said mods (ME3Tweaks Mod Manager).

These tools are practically required to mod the trilogy (excluding texture-only mods), and all three of the developers know the engine internals in ways that many mod developers don’t – for example, the flags in package headers, various file formats, how property serialization works in our games, Unreal’s memory system, etc.

At this point in development of Legendary Edition, there is not a lot that BioWare can internally change to help with modding. You cannot bolt proper mod support onto a game, it must be done from the start, and nobody reasonable expected BioWare to do such a thing.

 

What are my concerns?

Ever since Mac Walters tweeted about working with the modding community, I have had dozens of people message me directly about how they’re excited to have existing mods work on legendary edition. Many mod authors are also excited to work on legendary edition.

I do feel that there is an expectation of modding Legendary Edition that is not actually based in reality, and it’s going to cause issues if the game turns out to not be so easy to work with. End users will blame both us, the tool developers, and BioWare, as they feel they were mislead.

They will also blame mod developers because BioWare’s statements made them think this was easy – we have users already come for assistance asking why these games are not easy to mod like Skyrim.

 

But can BioWare do anything to help?

BioWare for nearly a decade has seemingly taken a ‘do not acknowledge mods’ policy, so for them to say they’re working with the mod community out of the blue is a real whiplash for the scene. I would love to have a conversation with BioWare to discuss things that could help the modding scene that should not take a lot of effort to implement, but I’ll detail some of them below.

 

1. We need a way to debug the game

ME1 on PC included the application log functionality, which wrote a bunch of useful information to the Documents/BioWare/Mass Effect/Logs folder, including crash information (such as loading an unused mip).

This log functionality was removed in ME2, and all internal debug code also was stripped out, so debugging ME2 is nearly impossible.

ME3 included a blank stub for ‘appErrorF’, which we hooked to get a basic level of debugging, but it’s still pretty weak and often doesn’t have any useful information. Complicating things was the use of TOC files which for years added extra steps to modding that were not required for ME1/ME2.

If you want us to be able to more easily mod the game, we need a way to know what the game engine is doing, or is unhappy about. Can you imagine writing software where the only way you know something is wrong is when the application unexpectedly closes? Because that’s what modding is often like.

We may have advanced tools these days, but when it comes to debugging and fixing a mistake you made, it can be such a frustrating experience that you just give up. If Legendary Edition removes the logs feature and strips out debugging tools we can leverage, it’s going to seriously cripple the ability to mod these games. Even if logging functionality required another edit to enable (such as through the configuration files) it would go a LONG way in helping us.

 

2. WE NEED A WAY TO LOAD FILES IN AN OVERRIDE FASHION

In the current scene, we load files primarily through the use of the DLC loading mechanism for ME2 and ME3. We recently (about 2 years ago) uncovered some tricks in ME1 to also load DLC overrides in ME1, but it’s weaker and has some caveats, such as not working with movie files and the TLK files being embedded into a billion files (though I doubt this is an issue, as the PS3 version of ME1 did not use this system).

There are some core files that can’t load from DLC, such as SFXGame, which is a real problem because it has a lot of crucial edits. One system that I personally leverage is TESTPATCH in ME3, which can override classes, but we don’t understand how it works well enough to generate new entries.

If the DLC system is removed – which it very well could be, because if this is the ‘best’ version of the games, they won’t need to have post release content, and thus there’s no need for DLC loading – it’s going to greatly complicate modding. Replacing files in the main game is extremely easy to do wrong, leads to wide incompatibility, and is frustrating to support; I know this because for years I wrote multiplayer mods, which required file replacement.

DLC mod loading allowed us to override and remove mods fairly easily. There are plenty of downsides but it is a huge improvement to direct file replacement.

 

3. Moral support? (optional)

For nearly a decade, modders have built loads of mods – Mass Effect Happy Ending Mod, controller support, bugfix mods, new levels, same gender romances, texture overhauls, etc…

Personally I’ve built about a dozen mods, made many programs, even built an entire web application that can be used to build mods – but I can count on two fingers the amount of times I’ve ever had my work acknowledged by someone at BioWare in any sort of way that actually meant something, beyond essentially them saying ‘cool’.

This is a somewhat weird thing to bring up, but if you look at the creative scene for Mass Effect, there are many areas you look at – artwork, cosplay, streamers, etc, but the modding community is by and large completely ignored, and it is somewhat demoralizing.

We build these mods because we love these games, even if we at times are very critical of your work. This modding scene was actually created because of the critical reception to the end of Mass Effect 3, but it’s because those people loved the series, not because they hated it.

There are likely a lot of legal ramifications that bar you from coming out and endorsing mods, plus a lot of things like ‘is this a reputable person’, etc. that are not really present when you post on a picture on Twitter that someone made. I also know that by tweeting or coming out saying ‘What a nice mod this is!’, people would almost assuredly misinterpret it as you ‘supporting’ mods, rather than just ‘supporting the idea of them’.

That said, it would be nice if the work we do could at least have a way of being acknowledged. For years we have been mostly ignored, and while I’m sure some developers are fine with this, it would greatly improve modding morale if we at least knew someone at BioWare actually cared, or at least thought it was interesting, even off the record. I’m sure there are some people that make these games that do enjoy our work, but you would be hard pressed to find anyone who develops mods that knows that.

 

4. WE NEED A WAY TO IDENTIFY WHICH FILE BELONGS TO WHICH GAME

Yes, this is a real nitpick, but it’s actually come up when trying to parse files from PS3 and Xbox assets of ME games. If package file headers could have some way to identify their game (something unique, like package tag) it would greatly reduce complexity. It seems small but as someone who works on tools, knowing what game a file belongs to is VERY IMPORTANT.

This is of course assuming that you are unifying the engine versions, which I assume you are, since this was already done for the PS3 versions of the games.

 

Future plans

I plan on working on legendary edition, but I want to make it clear to end users AND mod developers that things are going to take time. One of the things that greatly concerns me is how news sites are saying how great this game will be to mod based on just a few tweets and an interview that did not really talk about modding.

I want to make sure users and developers understand that modding will take time, will not work on day one, and likely will not work on month one, and will likely have serious teething issues possibly for years.

As someone who works at the top of the modding chain, a lot of the development efforts have fallen to me – a developer needs a feature in Mod Manager for their mod? I have to implement it. Need an easier way to edit game files? One of the toolset developers typically has to do it – and we do this for free on our own time.

The other developers I work with (SirCxyrtyx and Kinkojiro) are great people who have all done years of research and development, and together we’ve built lots of great tools and features, such as Pathfinding Editor, entry porting and relinking, dialogue editing tools, and more.

I personally spent YEARS developing and refining Mod Manager and have spent years completely reworking ME3Explorer into a much more professional grade tool. Our scene is much healthier than it was back in 2013-2015 when our tools were still immature, making and using mods is far easier these days. Some of this progress is going to be reset with the release of Legendary Edition.

 

WILL EXISTING TOOLS WORK ON LEGENDARY EDITION?

Short answer, no. We will have to wait and see. I do not have any plans to have ME3Tweaks Mod Manager work on LE – it will require me to build a new tool, as M3 is very tailored to the current games, tools and codebase. ALOT Installer will not be necessary (hopefully!), and ME3Explorer may be forked into a new project depending on how much has changed internally, but we don’t really know anything useful yet. 

Personally, I am not a fan of trying to build a conversion tool between the trilogy games and legendary edition, as it would be a monumental amount of work, especially if we don’t understand changes to the internals of the games (including many things we currently don’t know about the existing games). The toolset already supports about 7 or 8 games across 4 different platforms, adding another 3 will be significantly complicate the codebase. But it really depends on how much is changed.

 

Anything else?

Yeah, the last thing I’d like to mention, is that a lot of this post is about the developer side of things. Myself, SirCxyrtyx, and Kinkojiro have all done absolutely masochistic things in the name of research and development (but especially Kinkojiro), but the flip side is that there are far more users who have little to no patience even installing mods.

If installing mods is difficult (such as a complicated system without a DLC loader), many users will not even attempt it, and this leads to diminished returns on the development side. I work on both sides of the coin, writing the tools for installing mods, as well as developing them, and over time  I’ve adjusted my software to make it easier for users.

I’ve noticed that as it becomes easier to install mods, more developers have appeared, and part of it may have to do with decreased support time for mods. My end-user tools for example have a very detailed diagnostic system that can be used to identify a wide variety of issues, which means users don’t have to ask developers how to fix things.

 

CONCLUSION

I believe our modding tools for legendary edition could be greatly improved if we had someone we (the modding toolset developers) could talk to at BioWare about, as there are still many gaps in our knowledge of BioWare’s proprietary changes to the engine that significantly hinder our efforts. With the release of Legendary Edition, those knowledge gaps will likely significantly widen. 

Want to have a talk about some other things the scene could use BioWare? Drop me a line at femshep AT me3tweaks.com, there are many small things that would greatly reduce the friction of modding that would really help set up the scene for success.

Hi all, it’s me again, your favorite modder who publishes a single research blog post a year. Welcome to my new blog, where I will also post maybe once a year! I got fed up with blogger’s endless unfixed bugs. I’m going to leave the content there though for historical sake.

I just finished a hardcore crunch to ship ALOT Installer V4, which is a complete rewrite of ALOT Installer. ALOT Installer is the Mass Effect modding scene’s main texture installation tool, built on top of aquadran’s MassEffectModder program, which can be used to install textures in a more advanced fashion. In V4 of ALOT Installer, I split the main ‘core’ features into a cross-platform .NET Core library so I can also write a frontend that works on Linux. But that’s not why I’m here today – I’m here to follow up on how I fixed Mass Effect on PC to not require elevation for good.

Mass Effect on PC: About what you’d be expect from a mid 2000’s console port

For those of you not in the know, Mass Effect came out on PC back in 2008, and was ported from the Xbox 360 by a studio named Demiurge, who also developed Pinnacle Station for Mass Effect. It’s… a really meh port that has not aged very well. It’s passable as a game but it has a lot of problems, even when it came out. Particle LODs not working properly, texture LODs being read backwards, ini settings being randomly reset to their defaults, the problems are pretty numerous, just to name a few. But nothing completely game breaking.

Well, kind of. There is one, but it’s not specifically due to Mass Effect. The big issue is that Mass Effect requires administrator rights to run, because Demiurge seems to have assumed everyone would run the game as administrator – which might have been OK if the game was only really developed when Windows XP existed, but Windows Vista had already been out for over a year by the time the game had released. Even back then though, Windows XP had a concept of LUA (Least User Access) with separated user accounts. For more information on this, you should check out the original post I wrote, Why Mass Effect on PC requires administrator. It describes a lot of backstory to this post.

Oh boy, PhysX, my favorite physics library!

I may have a slight beef with this SDK.

Mass Effect for PC runs on a lightly modified version of Unreal Engine 3, which appears to be dated around late 2006. According to some former BioWare developers, this version of Unreal Engine was not very mature yet, to put it lightly. According to some stories from these developers, it was really difficult to work with because Epic Games was focused on Gears of War and not dedicating much time to their partners who were also using the engine.

Unreal Engine 3 uses PhysX for physics interactions, so Epic Games built a dll that interfaces PhysX to Unreal Engine data formats through a file named PhysXLoader.dll, which loads the PhysX libraries from both parties. PhysX is a physics simulation library that was acquired by AGEIA Technologies in the mid 2000s before AGEIA was sold to Nvidia in early 2008. If you remember Physics Processing Unit cards, or PPU, they were using PhysX before Nvidia promptly killed that idea.

PhysXLoader.dll, PhysXCore.dll, and NxCooking.dll make up the PhysX dlls for Mass Effect.

All three Mass Effect games use PhysX, but Mass Effect 2 and Mass Effect 3 use the system’s install of PhysX, while Mass Effect uses the local game’s PhysX. Mass Effect 2 and Mass Effect 3 also use the ‘modern’ version of PhysX, rather than the legacy one that was shipped by AGEIA. Nvidia changed some paths under the hood when it took over, which separates Legacy out from it’s ‘modern’ versions.

But that doesn’t seem to stop Legacy PhysX’s uninstaller from deleting modern PhysX’s files/registry keys, so during the course of testing this fix, my other copies of Mass Effect 2/3 didn’t work, even after installing the ‘modern’ PhysX redistributable. It’s really annoying how BioWare couldn’t just ship a 8MB library with the game – they already shipped the installer for PhysX with the game, so it’s not like it saved space!

But anyways…

The issue with Epic Games’ PhysXLoader.dll is that it can load PhysXCore.dll locally, or from the system’s installed version

Err… wait, how is that an issue? Can’t you just load the local dll, and if that doesn’t exist, load the system one? How is that an issue exactly?

OH BOY HERE WE GO
You won’t believe how many facepalms there were as I making this fix.

On boot, Mass Effect writes two values to the Windows HKEY_LOCAL_MACHINE registry:

REG_BINARY HKLM\SOFTWARE\AGEIA Technologies enableLocalPhysXCore [mac address, 6 bytes]
REG_DWORD HKLM\SOFTWARE\AGEIA Technologies EpicLocalDllHack [1]

*Mass Effect is a 32-bit program, so on 64-bit systems it goes into HKLM\SOFTWARE\WOW6432Node\AGEIA Technologies instead, if you’re looking for yourself.

Remember these registry values, they’re going to be important later!

These registry values are why Mass Effect requires administrative permissions. In my previous blog post linked above, we explored why these writings were enough to make Microsoft put Mass Effect into it’s compatibility database, which forces it to run as admin when matching on certain executable criteria, which we worked around by modifying the executable criteria to no longer match.

We have to modify the executable to enable Large Address Aware, so the game could load higher resolution textures without running out of memory, so there was no way to avoid breaking the signature. This in turn caused Origin to no longer run the game as it would not elevate games without a valid EA signature. But if the game cannot write these registry keys on boot, the game may crash…

So it’s already a big fun chain of problems, but we worked around Mass Effect needing administrative rights by simply giving the user account permissions to that specific AGEIA Technologies registry key. This would let the game process write the values it needed, and would we could go on our merry way. I assumed the game crashed because it was denied write permissions and Demiurge couldn’t be bothered to write a try/catch around the registry writing code.

You probably shouldn’t name your registry values as a hack if you want me to think this is a good idea

Our solution to this problem did not change Mass Effect’s behavior – the values it wanted to write to the registry were going to be written one way or another, so we were just letting it do the thing it’s always done, just without administrative rights. There wasn’t really any change in application behavior.

The two registry values that Mass Effect writes.

mirh, a moderator for PC Gaming Wiki, sounded the alarm for years that somehow we were breaking other games in ALOT Installer – even though our application didn’t actually change how Mass Effect was behaving writing these values, so there’s no way our change would break other games.

After many months, he wrote a fairly detailed reason why ALOT Installer (when, in reality, it was Mass Effect) is breaking other games: enableLocalPhysXCore being in the registry is used by other games using Epic Game’s PhysXLoader.dll. When I was writing V4 of ALOT Installer, I told mirh I would take a more serious look into his idea of a solution that would not break other games, even though at the time I did not really understand how a registry key with the system’s MAC address would break other games – or why it even used a MAC address to begin with.

mirh seems to have determined this enableLocalPhysXCore lets Mass Effect use the local directory’s PhysXCore.dll/NxCooking.dll, instead of loading the one from the installed PhysX redistributable. Mass Effect doesn’t install the PhysX redistributable, so it could not rely on it existing, so it needed to use the local libraries.

Hope you’re strapped in because this is where it gets really dumb:

The MAC address stored in in the registry by MassEffect.exe is read by PhysXLoader.dll and compared against your system’s MAC address to determine if it should load the local directory’s PhysX libraries or the system’s.

Which MAC address?

¯\_(ツ)_/¯

So the way Mass Effect works:

  1. Very early in the boot process of MassEffect.exe, your MAC address is read and written to the registry as enableLocalPhysXCore (along with EpicLocalDllHack)
  2. MassEffect.exe loads PhysXLoader.dll
  3. PhysXLoader.dll reads the value of enableLocalPhysXCore and compares your system’s MAC address against it
  4. If it matches, it uses the local folder’s PhysX, if not, it uses the system’s redistributable version of PhysX

Yes, you read that right.

It turns out that other games, such as Mirror’s Edge, have a PhysXLoader.dll that also reads these values (as they’re based on the same code), but they don’t include local PhysX libraries. So those games boot up, see enableLocalPhysXCore, and try to load the local library, which fails, and the game doesn’t start. This information is second hand from mirh – I have not tested other games broken by this registry value.

Normally that value wouldn’t exist, and it should use the system PhysX. This behavior can be tested in Mass Effect by denying it write permissions to the registry key, deleting the values, and having Legacy PhysX installed – it will use the system libraries instead. If system PhysX is not installed, the application will not boot – this is why we originally had to let Mass Effect write these keys, otherwise it could appear that the installer broke Mass Effect, when it actually was a terrible implementation by Epic Games.

Facepalm
It’s hard to imagine any possible scenario where this was a good idea.

If you’re interfacing with a library that has exports you can call to initialize/load the PhysX SDK… couldn’t you just, you know, pass a boolean to tell it to locally load? Why does it not locally look to begin with? And what’s up with the MAC address? Why is this in the registry, where it behaves LIKE A GLOBAL SETTING???

All of these seem like terrible design decisions – and after disassembling the PhysXLoader.dll, it seems like that’s all they were – terrible design decisions. Let’s take a deeper look at Mass Effect and walk through the process of fixing this, from start to finish.

Finding a starting point

WARNING: I’m a super novice reverse engineer. I’ve assembly modded Megaman Battle Network games (and wrote a pretty neat guide to how to design hooks), designed mods in ActionScript2 P-Code, and worked with UnrealScript bytecode, but never really got far into x86 assembly. I’ve opened IDA numerous times, and could kind of find what I was looking for, but never really could understand it. I’m sure this process is much easier for more experience reverse engineers.

Scary wall of IDA
It’s hard to get excited for reverse engineering when you have almost no idea where to start. This is IDA’s graph view, which helps a lot to visualize the assembly… but is still very hard to understand in a large 20MB binary.

Recently (as in the past 2 years), the National Security Agency of the USA (the NSA) released Ghidra, a free open-source reverse engineering toolkit that can reverse assembly back into somewhat-readable C code, which is infinitely easier to read than IDA assembly graphs. Both IDA and Ghidra have their strengths; IDA has a debugger that lets you step through the assembly and see which code paths are about to execute, and can find Unicode strings (which Mass Effect games use), while Ghidra can recompile assemblies from it’s decompiled C code (sometimes), has an assembly-to-C converter (sorry, I don’t know the name of this), is open source, and works on tons of platforms, binaries, you name it.

Ghidra logo
Ghidra is a great tool if you’re getting into reverse engineering as it lets you see the assembly as C code, though without variable names.

So the information I knew starting out was that Mass Effect was writing enableLocalPhysXCore and EpicLocalDllHack. Let’s start by looking at MassEffect.exe, finding these strings, and seeing what references them. Using a hex editor, I know these are unicode strings, so I’m going to look for them in IDA, since Ghidra doesn’t seem to support this.

IDA Strings Window
IDA Strings Window. I finally learned that Shift + F12 opens this useful tab.

Inside of IDA’s Strings window, searching for enableLocalPhysXCore shows the string. Double clicking it takes us to the data section of the executable it’s defined in:

IDA strings for enableLocalPhysXCore
You can see above where enableLocalPhysXCore, EpicLocalDLLHack, and even the registry key path is defined, all right next to each other.

Above we can see the definitions of the strings that all seem related to our quest. Above the text definition, we can see there’s a DATA XREF, which means something directly references this data – probably what’s writing it. Let’s double click the XREF and see where we go.

Shown in IDA View, rather than Graph View.

Looking at this, we can see it’s writing RegSetValueExW. I’m a very weak C developer, so after doing some googling, I can see that this is the stack being setup to invoke a method call from the Windows API in C, which you can somewhat see with the parameter name that IDA shows, such as lpData and dwType. We know that enableLocalPhysXCore’s value is set to your MAC address – lets see where that gets set. Let’s change to Graph View for a more logical look at this.

enableLocalPhysXCore's 'set' method

We can see in the third block that eax is pushed onto the stack for lpData, and it’s also pushed onto the stack for this mysterious ‘sub_10929393’ call. There are no other calls in this subroutine that don’t have defined names, so this is probably where the MAC is obtained. Let’s jump into it.

This seems to be some sort of wrapper subroutine, or perhaps something IDA does, but it just points to another subroutine. Let’s go there instead.
IP Adapter code

This subroutine has names that are pulled in from the Windows API that show us that this has to do with networking. We don’t really care about the MAC address, but this lets us define the name of this subroutine. We’ll call it GetMacAddress. We’ll go back to our original subroutine we were looking at, and rename that too – this seems to be something like SetupPhysXSDKLoad, so we’ll name it that.

Our renamed subroutines

This is a relatively small subroutine, and all it does is write the two aforementioned registry values, and that’s it. There are no other references to this subroutine beyond one early in the boot process – so at this point I’m fairly certain that Mass Effect’s executable never actually reads these values, and at this point I’m still not sure what EpicLocalDllHack does.

Cracking open PhysXLoader.dll

Now we know that Mass Effect’s executable never reads this key, it must be in one of it’s dll’s. While I won’t show it here, using ProcMon (a great tool for modding and doing things like this in general), I could see that the registry value was read right before the library was loaded in the MassEffect.exe process, and the local dll was loaded. I observed it reading the system one when I denied Mass Effect write permissions to this directory, and the game not loading at all when there was no system version of Legacy PhysX installed.

The first dll loaded is PhysXLoader, after which PhysXCore.dll loads, so that is our logical dll to analyze. Let’s crack this open in IDA and see where enableLocalPhysXCore gets used there. I’m also going to pop this dll open in Ghidra so I can get a better idea of what’s going on. Doing the same procedure as above for finding where enableLocalPhysXCore’s string is used, we find this subroutine:
PhysXCore.dll subroutine

This is not too hard of a subroutine to read, especially in graph view – we can see there’s a loop from the left box going to the box above it. Still, not the easiest thing for a novice to read, so let’s see what this looks like in Ghidra. I use the location of this subroutine to jump to it in Ghidra (0x10001640).
Ghidra at enableLocalPhysXCore usage

This gives us a bit more of an idea what’s going on – the subroutine calls, the loop, the return type of the subroutine. We can identify things in one tool and label them in both to help us figure out what we’re doing. Immediately we can see that there’s a subroutine call that passes a registry key path, a value name, as well as two variables. In the loop we can see it’s comparing 6 items by index to determine if they’re the same.

Just to make this a post a bit shorter, that registry lookup subroutine is indeed, a simple registry fetch wrapper. Its parameters after reverse engineering (which mostly was just mapping subroutine inputs to what they are for the windows api calls) are subkey, valuename, returned data pointer, returned data size.

You can see it has a loop that runs for 6 iterations, checking each value is identical. Using the information we’ve gained, we can rename some variables to get a better picture of what this subroutine is doing.
Partially reverse engineered subroutine

We know that Mass Effect wrote a 6 byte mac address to the registry, and that PhysXLoader.dll just read that value from the registry, and the subroutine is comparing something byte by byte 6 times. Logically, we can assume that in the above picture local_14 is the MAC address. Knowing this as well, we can guess that FUN_10001580 is fetching the MAC address and setting it, so we’ll rename some more items in this subroutine.

Now, this subroutine call seems to not do the actual loading – it is just checking for the key, and if the MAC address matches. Given the name and what we know about this key’s activity, we can give this subroutine an educated guess of a name of ‘ShouldUseLocalPhysX’. Comparing IDA and Ghidra’s decompilation of this subroutine however lead to slightly different results, with Ghidra’s seemingly being wrong:

IDA showing extra data that doesn't appear in Ghidra decomp
IDA shows that al is being set to 1 if the loop exits normally, and 0 (the xor al,al) if any bytes don’t match – Ghidra doesn’t show this, in fact it shows the return type is void, which seems to be wrong.

After doing some googling for this part of the post, I learned that EAX is typically used as the return register for x86, and the ‘al’ register is the bottom 8 bits of EAX. I’m not experienced enough with Ghidra to know how to retype the signature for this kind of lower 8 bits being returned, it may be just something Ghidra does not support currently, or I am missing a setting I was supposed to use.

Code for loading PhysXCore.dll
Disassembly of the subroutine that calls the one that is looking at enableLocalPhysXCore.

However, if we look at the references to this subroutine (there are two – likely one for each library) in IDA and Ghidra, we can see that ShouldUseLocalPhysX is being called, it’s checking if al is not zero. If it’s not zero, it loads the local PhysXCore.dll. If it is, it appears to look it up through the system PhysX installation, which is identified by another registry value in the AGEIA Technologies key named PhysXCore Path. We’re not really interested in that, since we’re looking to force PhysX to always load locally, regardless of the enableLocalPhysXCore being set or not.

Looking at the other cross reference, we can indeed see it’s loading the NxCooking library, using ShouldUseLocalPhysX in the same way:
NxCookingLoader

Armed with this knowledge, there are a few ways we could fix this. I’ve done a lot of function modding in UnrealScript bytecode, which for a very long time could not be extended or shrunk in size, so figuring out ways to make logic checks fail or pass without changing the size of the script was a challenge.

For example, if I needed an if check to essentially be removed, I’d need to find a way to change the comparisons to always be true or false. One way I could make an if check fail would be to modify the object references and comparison bytecode tokens to produce a condition such as if (objectA != objectA), will always produce false (assuming they are not null). We need to find a way in ShouldUseLocalPhysX here to always produce a true result.

When I was writing a symbol table for Megaman Battle Network 3, I learned to comment everything I figured out about the disassembly. I would work on something for hours, totally forgetting what I did, but could come back to my comments and understand it again.

Sometimes my symbols/comments on certain lines, such as subroutine names or pointer tables I’d identified, would come up in other subroutines, giving me useful context that I would not have seen otherwise. I’ve commented and labeled several items below, which make reading this subroutine easier.

Patching out the world’s worst boolean check

Notated ShouldUseLocalPhysX
We need to ensure the bottom left block is always reached – while also making sure we don’t touch the stack (which can EASILY nuke the whole program). Technically, we could just rewrite some of this subroutine, but I’d like to make the least amount of change possible here. We need to essentially make sure the jump to the bottom right block never happens.

Conveniently, x86 has a 1 byte instruction named ‘nop’, which does literally nothing but take up one byte. Also conveniently, the jump instruction to that block is 2 bytes, comprising of a 0x75 (jnz rel8) and a 0x19 (relative offset).
Hex view of the instructions

[FUN SIDE STORY] Seeing a 1-byte offset brings me back to my Megaman Battle Network modding days, where jump/branch instructions would make or break moddability for certain sections of the ROM. When writing a hook (which redirects the program counter to your own code), you had to find a jump or branch instruction, which you would modify it’s relative offset to point to your code. You would then push registers onto the stack, run your code, then put the stack back together so the subroutine exited like it should.

ARM (more specifically, THUMB) had limited branch instructions that used different sizes for their relative offsets, which were not able to always jump to any point in the ROM due to their location in the ROM. Since the game was written in assembly, finding free space at times could be difficult – sometimes you’d have to chain multiple hooks until you could get the program counter into to a free area to write new assembly code. This jnz uses opcode 0x75, which is jnz rel8, so it can only jump up to 128 bytes (or is it forward only, so 255 bytes?) away, which would be a real problem if I was doing assembly modding the way we used to back before we had fancy tools like IDA and Ghidra. [END OF FUN SIDE STORY]

Anyways, after we nop out that jnz to the not-matching block of assembly, our ShouldUseLocalPhysX subroutine now looks like this:
Patched subroutine

Now the not equal block cannot be reached. The ‘check’ still occurs, but it never can return false. The local PhysX core dll will always be used.

What are the downsides?

The PhysXLoader.dll file is signed by Epic Games, so this obviously breaks the signature since we modified the file. The game is not checking signatures on load, so that’s not an issue. Some antivirus programs may complain about broken signatures, but over time that typically improves. Outside of writing an in-memory patch (like we do with the asi mod loader our scene has), we would have to modify the binary of the library.

Final behavior

Using this patched dll, the game now works with or without the registry value, which means Mass Effect no longer needs administrative rights at all to run. Disassembling this was a real rant-fest because I could not get over the asinine way this check was implemented, not just a registry value but a match on MAC address too. While I was debugging and stepping through the instructions, I actually broke the game because I turned on my VPN and my MAC address it used changed.

This was a good learning experience, I’ve learned more about Ghidra and IDA, and about more problems in the PC version of Mass Effect. This patch is automatically applied as part of the install step of ALOT Installer, so users will never need to worry about having the enableLocalPhysXCore key set after it’s patched. We also modify the Mass Effect executable to write a value named enableLocalPhysXCor_, so our patched versions won’t write the value that breaks games. Vanilla Mass Effect executables will still break other games, but it is beyond the scope of our program to try and protect people’s software from poorly written PhysX loaders.

Essentially, this is how I felt afterwards:
Yeah...

Oh, and what about EpicLocalDllHack? Well… it does literally nothing. Absolutely worthless, it’s never read. The only thing I can think of that it could maybe exist for is to keep the registry key around if a PhysX redistributable is uninstalled, as it’s not part of the PhysX redistributable’s list of values – but that’s just a guess.

Was having a parameter ‘PreferLocalSDK’ for PhysXLoader.dll too much to ask for Epic Games?

This post originally appeared on my now deprecated ME3Tweaks Blog that was hosted on Blogger. It’s been ported here for consistency. This post was published originally on January 30, 2018 and has only been slightly modified to make it easier to read here. The contents remain unchanged.

———————

Hi all. It’s been some time since I’ve posted. No I am not dead. I have been doing a lot of work behind the scenes. Been working on my own ME3Explorer fork which has many quality of life improvements and even some new tools. I’ve also been working on Mod Manager 5.1 which has some slick new importing of third party mods, but it’s been on the back burner since I’ve been working on the new ALOT Installer front end.

ALOT Installer 2017
ALOT Installer using the 2017 manifest

I have been working with CreeperLava and Aquadran to implement this, which should make life way easier for end users installing ALOT and its Addon (textures by others). One issue I have had is that Origin will not launch the game once ALOT is installed unless run as admin. And since you cannot have origin startup as admin on boot it is really annoying. This also affects MEUITM. So I looked into figuring out why. It’s actually a perfect storm of security, bad coding, and people trying to make others lives easier.

Lets break down how Mass Effect works with Origin in a non-modified state, on Windows 10.

  • You run MassEffect.exe. It immediately asks for elevation to administrator.
  • At the end of the MassEffect.exe image there is code to call Origin to run the game, as a form of DRM. It calls origin, and then exits.
  • Origin checks your entitlement to the game and will then run MassEffect.exe as specified by the registry (not the one you booted specifically) and attempts to run the executable.
  • Origin is unable to run the executable because it requires elevation. In order for the DRM to work it must be able to interface with the process, so it elevates one of its internal services so it can communicate with the game for DRM purposes.
  • MassEffect.exe is run as administrator. Origin communicates with MassEffect.exe and game execution continues like it does from the DVD version.

Now, this all works (though two UAC prompts) on an unmodified game. But install MEUITM or ALOT and you won’t be able to run the game anymore through Origin as a standard user. What gives?

File signatures

Both MEUITM and ALOT modify the MassEffect.exe executable to use Large Address Aware. This allows the 32-bit Mass Effect process to use up to 4GB of ram instead of the usual 32-bit limit of 2GB. By modifying the LAA flag, the digital signature on MassEffect.exe is broken – the signature is used to verify the file is not modified. Once the file is modified, the signature is no longer valid.

Origin, when running an elevated process, checks to see if the EXE is signed by EA and is valid. If it is not signed by EA, it doesn’t elevate its DRM communication module. Mass Effect boots up, and then immediately closes as the DRM unlock doesn’t work because there is nothing to talk to it from Origin’s side as it refused to elevate.

So by modifying the EXE, origin will refuse to run an elevated game executable. But we need LAA, so we must work around this issue. Our only hope is to prevent MassEffect.exe from running as an administrator. We should figure out how it is set to run as administrator first.

Looking in the EXE’s manifest I can see it runs as the invoker – the user who is running the EXE. Which means this executable should not require administrator. I check my compatibility settings, nothing there either. Somehow, this is being elevated at boot but not through the exe itself nor my own doing. The culprit? The Microsoft Windows Compatibility Database.

mirh (I’ve seen him in some of the modding circles) did some sleuthing to figure out why Mass Effect is being forced to run as administrator. It meets the criteria in the database – it specifically has an entry for Mass Effect – which has its own always-forced compatibility settings. It makes sense – users shouldn’t have to configure the settings if MS already knows ones that (technically) work.

As you can see there are two entries for this game – MassEffect.exe (the game) and the launcher (which is not included in origin’s version sadly). The compatibility fix is RunAsHighest – which means administrator – and the criteria are:

  • EXE is named MassEffect.exe
  • Company name in manifest is BioWare
  • Product name in manifest is Mass Effect
  • Product version is equal to or less than 1.2.0.0.

This criteria matches all known versions of the game – I believe including pirated versions. So by matching all of these criteria, the exe is forced to run as an administrator. You can test this out easily by simply renaming MassEffect.exe to anything else and it will no longer require admin to run. (Origin won’t be happy).

The fix

So now we have an idea of how to fix this – but why is this entry here? Well, due to Demiurge/Bioware not following the idea of Least User Access (LUA), Mass Effect on its very first boot requires administrative privilege to write to the HKEY_LOCAL_MACHINE\SOFTWARE\AGEIA Technologies registry key. If this key does not exist, it will attempt to make it – without admin, it doesn’t have permission to, and the game simply crashes. This key seems to hold some sort of information about what is now known as PhysX. This could possibly have been done by the installer, but it seems they made it be done in the game instead.

This is why Microsoft forces the game to run as admin always – for this one single item. It makes sense – make it run as admin, user does not need to worry about compatibility settings. However due to this combo of all three – LAA breaking the signature, MS forcing it to run as admin, and Origin refusing to work with elevated processes that have a broken EA signature – Mass Effect does not run with Origin and LAA.

So what did we do to fix it? We simply modify in the EXE the product name from Mass Effect to Mass_Effect. Really, that’s it. It fails the criteria check and the game no longer needs admin and Origin is happy (except for that update nagging it always does). In both MEUITM and ALOT Installer we added code to make it create this registry key with write privileges for the current user, so if Mass Effect needs to make the keys (maybe its never been run somehow) it will be happy.