Web Rarely

Such a fine first dream! But they laughed at me; they said I had made it up.

Battle Brothers mod kit

2019-01-20

Background

I created a rudimentary mod kit for the game Battle Brothers that allows editing the encrypted Squirrel scripts (.cnut files) within the game. Since almost the entire game is implemented in script, this allows tremendous flexibility for modding. It started when I wanted to know which character backgrounds were best to hire in a "value for money" sense. There was a spreadsheet that the community had painstakingly created over the years based on hundreds of manual tests showing the typical stat ranges of various backgrounds, but it was invalidated by the Beasts & Exploration DLC which rebalanced various parts of the game. I wished I could just take a look at how characters are generated, but like many people trying to peek under the hood I soon saw that the scripts are shipped only in a compiled form, and not only that but they appear to be corrupted or encrypted.

Accessing the files

Before we can start messing with the game data files, we first have to know where they are. If your Battle Brothers installation directory is bbros, then the data files are stored within bbros/data/. Within that directory you'll see files named like data_001.dat. These are actually .zip files, and if you rename it to data_001.zip you'll be able to open it. Images, sound effects, etc., are not encrypted, so you can examine them. (Don't be tempted to modify them inside the .zip file, though. See below for how to make changes.) The scripts are the .cnut files, but if you open them they just look like garbage.

Encryption and decryption

The "corruption" in the compiled scripts is actually an unusual encryption algorithm that encrypts only bits and pieces of the files, making them look like normal compiled scripts at a glance, but preventing them from actually working if you try to run or examine them. After painstakingly reverse engineering parts of the game, I was able to decipher the encryption algorithm. I spent quite a long time discovering how it worked and then a while longer translating the cryptic instructions into C code that I could run. Only after I was done did I realize that one of the constants in the algorithm is well-known, and that the core cipher is XXTEA.

But like I said, only parts of the file are encrypted. The encryption algorithm is unusual in that the format of a Battle Brothers encrypted .cnut file cannot be described in a stand-alone way since it's intimately tied to the internal implementation details of the Squirrel script engine. Specifically, the Squirrel API calls to parse a compiled script take a reader callback function, which gets called with a buffer and a number of requested bytes, and the function is supposed to read those bytes from the application's storage. Basically, it's a byte-oriented stream interface. Now, where it gets tricky is that the game only decrypts runs of 8 or more bytes requested through that stream interface, and it doesn't keep any state between invocations. If the Squirrel engine changed to break one read into two, or to combine two reads into one, or even just changed the size of any encrypted read, the file format would effectively change. So, to be safe, you should use the same version of Squirrel as the game. At the time of writing, this is 3.0.4, but I've also tested with 3.0.7. Also, only multiples of 4 bytes are encrypted, so a 15-byte read has the first 12 bytes encrypted and the last 3 bytes unencrypted.

So, without further ado, here's how you'd go about decrypting a Battle Brothers .cnut file:

// the standard XXTEA block cipher algorithm
#define DELTA 0x9e3779b9
#define MX(p) (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[((p)&3)^e] ^ z)))

void decrypt(uint32_t *values, uint32_t count, const uint32_t key[4])
{
    uint32_t rounds = 6 + 52/count, sum = rounds*DELTA, y = values[0], z;
    do
    {
        uint32_t e = (sum >> 2) & 3;
        for (uint32_t p = count-1; p; p--)
        {
            z = values[p-1];
            y = values[p] -= MX(p);
        }
        z = values[count-1];
        y = values[0] -= MX(0);
        sum -= DELTA;
    } while (--rounds);
}

// the key used by Battle Brothers
static uint32_t bbkey[4] = { 238473842, 20047425, 14005, 978629342 };

// the Squirrel reader function
SQInteger read_encrypted(SQUserPointer file, SQUserPointer buffer, SQInteger length)
{
  length = file_read(file, buffer, length); // read the data from the file
  if (length >= 8) decrypt((uint32_t*)buffer, length / sizeof(uint32_t), bbkey);
  return length;
}

...
sq_readclosure(vm, read_encrypted, file);

To encrypt a file, you can't simply do the analogous approach of using a writer callback function to encrypt all blocks of 8+ bytes, since the Squirrel writer doesn't invoke the callback for the same offsets and lengths as the reader, which is needed for the two to match. (And even if they did match, there's no guarantee that it would stay that way.) So, I implemented encryption using a reader function as well, so you read the file that you want to encrypt, and as a byproduct of reading it the file gets encrypted.

void encrypt(uint32_t *values, uint32_t count, const uint32_t key[4])
{
    uint32_t rounds = 6 + 52/count, sum = 0, y, z = values[count-1];
    do
    {
        sum += DELTA;
        uint32_t e = (sum >> 2) & 3, p;
        for (p = 0; p < count-1; p++)
        {
            y = values[p+1];
            z = values[p] += MX(p);
        }
        y = values[0];
        z = values[count-1] += MX(p);
    } while (--rounds);
}

SQInteger read_and_encrypt(SQUserPointer file, SQUserPointer buffer, SQInteger length)
{
  length = file_read(file, buffer, length); // read the data from the file
  if (length >= 8)
  {
    // encrypt the file in place. you could also write the changes to a separate file
    encrypt((uint32_t*)buffer, length / sizeof(uint32_t), bbkey); // encrypt the data
    file_seek(file, -length, SEEK_CUR); // seek back to the start of the block
    file_write(buffer, length); // write the encrypted bytes back to the file
    file_flush(file); // some C file APIs require a flush when switching from write to read

    // decrypt the data again so we don't return garbage to Squirrel
    decrypt((uint32_t*)buffer, length / sizeof(uint32_t), bbkey);
  }

  return length;
}

Decompiling a script

Now we can decrypt a script file, but it's still in compiled form, so we can't easily edit it. To decompile it, I recommend using NutCracker. I actually don't like that decompiler, because the output is incorrect. It has numerous bugs, some of which I've fixed, and is more verbose than it needs to be. It also doesn't preserve all the information from the .cnut file, so if you decompile a file with NutCracker and then recompile it, the result will not be the same as the original. It still seems to work when loaded into the game, though.

Because of those problems, I wanted to write my own decompiler. I'm sure I can create a better one than NutCracker, but my free time is limited so I probably won't do so unless there's a real demand. The problems above are not fatal, since Battle Brothers is capable of loading plain-text scripts (for the most part) and most scripts decompile correctly. A few scripts don't, though, so be careful.

Anyway, here's an example of how to use NutCracker to decompile a script. By default it prints the script to the console, so you simply redirect the output to a file. (The NutCracker author doesn't distribute binaries for the decompiler, but a binary is included in my mod kit.) From a command-line:

nutcracker decrypted.cnut >decrypted.nut

Getting a modified script loaded by the game

Lets say you decompile scripts/items/weapons/knife.cnut, which starts like this:

this.knife <- this.inherit("scripts/items/weapons/weapon", {
	m = {},
	function create()
	{
		this.weapon.create();
		this.m.ID = "weapon.knife";
		this.m.Name = "Knife";
		this.m.Description = "A short knife, not made for combat.";
		this.m.Categories = "Dagger, One-Handed";
    ...

Just for a test, you change "not" to "totally" within the description. Now how to get the modification back into the game? The most convenient way is to just use the plain-text script. For many scripts (including this one), the game will prioritize a plain-text script named "foo.nut" over a compiled script named "foo.cnut" in the same directory, so you could drop the plain-text script into the directory with the .cnut file. But to be sure you can recompile the .nut file using the standard Squirrel compiler (sq.exe) into a .cnut file and then reencrypt it using the mod kit.

You could simply insert the file into the data_001.dat archive, but that's a bad idea, because your modification may cause file corruption if the game is patched, and any game update would undo your changes. We want the game to load the base data and your modified version. Thankfully, the game uses the PhysFS filesystem abstraction library, and configures it in such a way that the game will load files from subdirectories of the data directory as well as files from the .dat archives. So you could actually place your modified knife.nut (or knife.cnut) file in bbros/data/scripts/items/weapons/ and it will take precedence over knife.cnut from the data_001.dat archive. This is good for testing, but is not a good solution for mods in general because it makes it hard to combine multiple mods. (Their files would be all mixed together.)

A better way is to simply package your mod into its own .dat file. Since the .dat files are really .zip files, just create a .zip file with the scripts/items/weapons/knife.nut file inside and place it in the bbros/data/ directory along with data_001.dat. (It's not necessary to use the .dat extension.) The important thing here is that mods get loaded after the base data, and I believe this happens based on the file name. Since 'm' sorts after 'd', naming the archive "mod_knife.zip" hopefully ensures that it gets loaded after the file named "data_001.dat". If order among mods is important (to resolve some conflicts), you could tweak the names to get the right ordering.

Coding guidelines for better mods

Now we can mod the game, but mods created as above will not play well together either with other mods or with future official updates. The reason is that you are overwriting entire files rather than just tweaking the bits you want to change. If an official update changes the file, your copy won't have the update unless you recreate your mod based on the latest official version. And, two mods that update the same file will overwrite each other's changes even if the changes don't conflict. So, we need a way to express the changes relevant to us without duplicating the rest of the file. Consider the following script.

local super = this.knife.create;
this.knife.create = function()
{
  super();
  this.m.Description = "A short knife, totally made for combat.";
}

Something like this would allow us to change just the description while keeping the rest of the knife code the same, and we could put this script into its own file rather than overwriting the base knife file. The difficult part is getting this code loaded at the right time. By placing your file in a folder named !mod_mymod you can make a script load before most others, and by placing it in a folder named ~mod_mymod you can make it load after most others, but neither of those work in this case. That said, I did create a small mod called "modding script hooks" that allows something like this:

::mods_hookNewObject("items/weapons/knife", function(obj)
{
  obj.m.Description = "A short knife, totally made for combat.";
});

On a side note, any sizeable mod will have to deal with official updates. You wouldn't want to redo your mod from scratch after each official update, nor would you want to continue to use outdated script files in your mod. The solution here is to set up version control. Extract the game data, decompile the scripts, and put it into a Mercurial or Git repository. Create a branch for the mod and edit your mod in the new branch. After each official update, switch back to the main branch, extract and decompile the game data again, and check in any changes. Then merge the changes into your mod branch. This way you keep your mod up to date with official changes without having to redo it from scratch. Also, the repository should of course just be on your local disk. Don't go putting all the game files up on GitHub. This also helps if you need to merge multiple mods together due to conflicts.

The mod kit

I packaged together a few tools into a very basic mod kit to get people started modifying the scripts for Battle Brothers. See the README.txt file within the package for details.

Download: bbros.zip (874kb) Version 9 - released February 12, 2019

Changelog

  • Version 1, January 20, 2019 - Initial release
  • Version 2, January 21, 2019 - Attempted to fix a serious bug in NutCracker. I'm not sure if the fix is correct. Hopefully the author will make an official fix.
  • Version 3, January 22, 2019 - Attempted to fix another bug in NutCracker.
  • Version 4, January 23, 2019 - Attempted to fix another serious bug in NutCracker.
  • Version 5, January 26, 2019 - Added a program (bbrusher) that can unpack and repack the game's sprite sheets
  • Version 6, January 27, 2019 - Fixed two more serious bugs in NutCracker
  • Version 7, January 28, 2019 - Expanded the types of sprites that can be added with bbrusher. (Should be all types now.) Made Nutcracker's floating point output prettier. Added a masscompile.bat tool.
  • Version 8, February 3, 2019 - Fixed four more serious bugs in NutCracker
  • Version 9, February 12, 2019 - Improved Nutcracker's handling of non-ANSI strings by using UTF-8

Postscript

So in the end, I got my updated character backgrounds spreadsheet. :-) I wrote a program to scan the script files and make a spreadsheet out of it. I guess that's mission accomplished!

Comments

Who are you? 2019-01-20 11:34PM
What have you done?

you hab changed the construct of the universe for generations to come.

However you have wrote instructions at a high-level skilled programmers voice.

Are you able to dumb down the words in the Battle Brothers mod kit for us without the proper training?

Thank you and still can't believe you mod Sid Meiers Pirates game one of my favorites.

Wish we can get attacked by pirate hunters a full fledge fleet like maybe 1-3 ships trying to sink you or something.
an anonymous vinilly
RE: Who are you? 2019-01-21 05:42AM
I do hope people use the mod kit to make something cool. I'm probably too old now to make actual mods with it. I just wanted to enable other people to do so.

On those lines with Pirates! I remember I was once working on a mod to allow you to have up to 4 ships in combat. (The enemy could also have up to 4 ships.) I never finished it but it would have been interesting...

http://www.hookedonpirates.com/forums/viewtopic.php?t=6771

Then there's this craziness:

http://www.hookedonpirates.com/forums/viewtopic.php?p=91296#91296

Fun times. :-)
my hero 2019-01-21 06:25AM
Hi mate, i have spent literally hundreds of hours battling with those encrypted cnuts. I made Battle Sisters and the ork mod. This work is an absolute life saver. Thank you so very much.
an anonymous Poss
RE: my hero 2019-01-21 09:05AM
Hi Poss. I checked out your mods. Nice work on that art! I wish I was more artistically capable. I think it should be possible to flesh out your mods a bit now that you can edit scripts. For instance, making sure heads and bodies have the same sex, no beards on women, etc. Or, modifying character gear and traits to be more greenskin-like.

There's already some support you could latch on to, since the game has different head, body, and sound sets for the different races, and also for subsets like "old men", "smart men", "tidy men", "untidy men", etc. Women could just be another race or perhaps a different subset of humans. You also wouldn't have to lose any of male art by overwriting it. It could be in addition.

Anyway, I'm looking forward to seeing what you can make. :-)
Error decompiler 2019-01-21 11:57AM
Hi. I get an error when trying to decrypt files.

bbsq.exe -d orc_trophy_item.cnut a.nut
File a.nut had error the index 'std_stream' does not exist

Any idea how to get it working? Btw you are incredible for getting this done. Thank you
an anonymous Rafa
RE: RE: my hero 2019-01-21 01:22PM
I have edited every image in the game, and read through the bytecode in every line in every cnut and js file, looking for anything i could use. I had a rough idea of the structure of the game logic including races, units and how they call the different heads and hair. I just couldn't actually change any of it until now. It was like peering through a thick mist filled with "TRAP" sprinkled through the byte code.

I'll be able to do so much with the knowledge you've posted here, although those bugs you've found in the decompiler are like landmines in the path of any large mod. This is more progress than any modder has made before you.

Many won't be able to follow in your footesteps though, so if you're willing to do more work on this I'm happy to offer whatever support i can to help you. The complexity of this solution greatly limits how many modders will able to try it out.

I have heard many people say they would pay for mod support on this game, including myself. We can get a patreon or crowd funding campaign going if you're interested. I would personally be willing to pay your hourly rate directly to work on this if it got us to a single UI mod tool, or even a better decompiler
an anonymous Poss
RE: Error decompiler 2019-01-21 05:10PM
Hi Rafa. bbsq.exe is not the decompiler. It's just the decrypter. NutCracker is the decompiler. Normally you'd just use massdecompile.bat to decrypt and decompile all the game scripts at once, but if you just want a single script the pattern is:

bbsq.exe -d orc_trophy_item.cnut
nutcracker orc_trophy_item.cnut >orc_trophy_item.nut

Note that since you already ran bbsq.exe it's already been decrypted. Running bbsq.exe a second time will corrupt it. So you'd best start with a fresh copy of the .cnut file.
RE: RE: my hero 2019-01-21 05:16PM
I admire your persistence. I "only" spent about 8-9 hours cracking the encrypted .cnut format, but I was getting pretty sick of it by the end. :-P

I think I fixed the major bug I found in NutCracker, so I hope we are good to go. There are other bugs, though... I hope the author patches them, or else I might have to do it.

I'm not sure what you mean by a "UI mod tool". Theoretically you can do everything with the mod kit I posted, but I realize it can be a pain to edit scripts and HTML and CSS by hand. I suppose one could create a UI tool to let people edit simple things like item stats and character backgrounds. That wouldn't be hard. But any serious mod will probably have to proceed by writing code...
DONATION LINK 2019-01-22 05:03AM
Where can I deposit my donations for this awesome work?
an anonymous vinilly
Thank You 2019-01-22 07:00AM
Adam,

Thank you very much for these mod tools. This is fantastic. Already knee deep into the code with some ideas I've always wanted to do.

Again, greatly appreciated!

Any chance you have been able to get the log file statements in the scripts to print out to file? So far no luck.
an anonymous Rule
Problem with modifying item Script 2019-01-22 07:55AM
Hello There Adam,

first of all Thx a bunch for all the effort you put into this. Finally seeing some mod support was hell of a good news for me today ;)

I tried to follow your Guide to get into all this but unfortunally it didnt work out for me.
I became kind of stuck on point 4 of your suggested workflow in the Readme.txt.
I have all the ".nut" files ready in clear text by using the "Massdecompiler.bat" and for a simple testing purpose i just wanted to creat a modiefied "knife.nut" and change the description of the knife just as you have stated it in your example.
I modified the knife.nut file and placed it in my installation folder that looks now like this:

<InstallationFolder>\Data\scripts\items\weapons\knife.nut
The actually file that i used for the test is this:

https://nofile.io/f/jPU3UFEfo1r/knife.nut

Problem is, when i start a new game, im stuck in the loading screen. Waited 10min+ but nothing. No error massege visible. It just wont load the game.

I tried a different approach by packing the folder into an zip archive named data_005.dat but in this state the game wont even launch at all.

Im running the 1.2.0.25 GOG version of the game with Beats&Exploration DLC installed if this matters but i think i clearly miss somthing here.

Any kind of help would be much appriciated.
im desperete to get starting on that stuff. ;)

But for now saying Thx again and keep up the great work !
an anonymous Julian
RE: Problem with modifying item Script 2019-01-22 08:56AM
Interesting. Looks like another bug in NutCracker. I will file another bug against it. My guess is that the problem is caused by the way numbers are rendered in your culture. Instead of 32.0 the number appears like "32,00000000.0". That's not valid Squirrel code, of course.

If the NutCracker author doesn't fix it soon, I will fix it myself.
RE: Problem with modifying item Script 2019-01-22 10:24AM
Hi Julian, I tried fixing the problem in NutCracker. Please download version 3 of the mod kit and give it a try.
RE: Thank You 2019-01-22 10:26AM
Hi Rule, I think the log statements go into My Documents\Battle Brothers\log.html
Some thoughts and questions on modding scripts 2019-01-22 10:53PM
I guess one way to get along with the script loading order issue (mentioned in "Coding guidelines for better mods") is to write everything in dlc\dlc_unhold_supporter.nut. This file is located in data_005.dat and I guess it shall be loaded by the engine after all other scripts are finished.

Also I have some questions: lots of scripts start with a list (or tuple?) named "m" to define some constants. For example:

this.asset_manager <- {
m = {
Stash = null,
OverflowItems = [],
CampaignID = 0,
Name = "Battle Brothers",
Banner = "banner_01",
BannerID = 1,
Look = 1,
...

How can I modify those values other than making a copy of the entire file?

Thanks a lot for your time and critical insights.
an anonymous Liaoran
RE: Some thoughts and questions on modding scripts 2019-01-23 02:48AM
Hi Liaoran, the problem with script load order is that I think many scripts are loaded on demand the first time they are accessed. E.g. the first time a character gets spawned with a warbow, that might be when the warbow script gets loaded. So it's not a fixed order.

As for how to modify them without making a copy of the entire file, see my example for the knife above. If you can get it to load at the right time, you can modify them without copying the file.
RE: Some thoughts and questions on modding scripts 2019-01-23 02:50AM
Specifically, you'd make a script like like:
this.asset_manager.m.Name = "Battle Dudes"";
Compile and decompile 2019-01-23 04:35AM
Hi Adam,

When I compile an edited .nut file and then decompile the resulting .cnut file I notice there are a few issues. Besides the floating point one, it throws away iterator increments

- for( local i = 0; i != this.Const.Attributes.COUNT; i = ++i )
+ for( local i = 0; i != this.Const.Attributes.COUNT; i = i )^M
an anonymous Rule
RE: compile and decompile 2019-01-23 05:07AM
To be more specific, in the for loop constructor it does move the "++"/"--" increment to the end of the loop. But variables that are incremented/decremented are lost.
an anonymous Rule
RE: RE: Problem with modifying item Script 2019-01-23 06:53AM
Hello and thank you for your quick replie on that matter Adam,

im not that much of a coder myself but i think i see what the problem was here.

im from germany and this is absolutly not the first time i ran into problems with the "," and "." seperators when it comes to modding stuff so thanks for your explanation.

I also testet your Version 3 of the ModKit and as far as i can tell everything works like a charm now ;)

Guess i can now stop to hunt down and correct those floats manually in every script i wish to modify. That makes life a little easier over here :)
an anonymous Julian
Dev response 2019-01-23 09:04AM
Amazing work Adam (and bread!).

You might be interested to see one of the developers positive response to the mod-kit.

https://www.reddit.com/r/BattleBrothers/comments/ainxfh/i_asked_the_battle_brothers_devs_what_they/
an anonymous Al
RE: Compile and decompile 2019-01-23 09:14AM
Hi Rule. Yeah, the NutCracker decompiler isn't that great. I didn't write it, but any bug that changes the logic is a serious issue and I'll try to fix it myself if the NutCracker author doesn't.

Now, is it actually changing the logic or just changing the style? What is "lost" in your example? You said it moves the update to the end of the loop, which might be okay.

for(local i=0; i<10; i++) { body; }
for(local i=0; i<10; i = i) { body; i++; }

These are equivalent, as long as 'body' doesn't have a "break" or "continue" statement in it.

Anyway, please contact me by email and give me an example.
RE: Dev response 2019-01-23 09:51AM
Hi Al, thanks for the link. I did see the developers' response, but I didn't know word had traveled to Reddit. It was interesting to read the threads and comments and I wanted to respond to some of them, but I'll probably never create Reddit account. :-P Anyway, thanks again.
Is it possible to use .js files to hook scripts? 2019-01-23 05:26PM
As we know the .js files are easy to modify. Is it possible to call some functions or modules defined in scripts using those .js files? If so then the load order issue can possibly be solved.
an anonymous Liaoran
RE: Is it possible to use .js files to hook scripts? 2019-01-23 09:31PM
Yes, the JavaScript and Squirrel code can call each other to some degree.
RE: RE: Is it possible to use .js files to hook scripts? 2019-01-23 09:59PM
Then could you please show us a simple example? Personally I'm not an experienced programmer but I think I can learn by seeing how this is realized.
an anonymous Liaoran
Running modkit on Linux? 2019-02-01 09:24AM
Has anyone managed to get this running on Wine or compile it on Linux? I've tried the former but bbsq does nothing when called and gives me no error output with "bbsq -d whatever.cnut", if I instead run "bbsq -d whatever.cnut > whatever.nut" or massdecompile.bat I get "File $filename had the error the index 'std_stream' does not exist." in the case of massdecompile.bat this refers to tmp.cnut. Attempting to use nutcracker gives me "Error: Accessing non valid stack variable.".

Any help or should I resign myself to the fact an unofficial modkit for a Windows only game isn't going to be running on Linux?
an anonymous Faeren
RE: Running modkit on "Linux"? 2019-02-01 01:46PM
Hi Faerun, I assume by "Linux" you mean the GNU operating system (AKA "GNU/Linux"). Anyway, it sounds like you've gotten them running to me, since the error messages are normal. But the usage is incorrect.

"bbsq -d whatever.cnut" doesn't do nothing. It decrypts the file. It simply doesn't print anything in the success case. "Error: accessing not valid stack variable" from NutCracker indicates that the .cnut file is corrupt, either because you didn't decrypt it, or because you decrypted it multiple times, or something like that.

Try starting with a fresh .cnut file taken from the game's data file, and run these commands:
bbsq -d foo.cnut
nutcracker foo.cnut
It should work.

As for getting the mod kit running natively on GNU, it is entirely possible. The source code is included, but you'll have to compile it. bbsq and sq should compile without much difficulty. Follow the instructions from the Squirrel project. bbrusher is a .NET executable so you can run it directly under Mono without recompilation. The two .bat files could be replaced with bash scripts.
Hi 2019-02-05 03:10PM
Hi Adam, if not too much trouble could you upload your mods here? I don't have an account with nexusmods, thanks
an anonymous elderly gamer
RE: Hi 2019-02-09 07:49PM
Hi, elderly gamer. I'll note that a NexusMods account is free, but I don't mind hosting my mods on my website as well. That said, I will probably wait until they've stabilized before uploading them here, because it's not an easy thing to do and I only want to do it once per mod. :-)
Question for the modding script hook 2019-02-13 08:27PM
I am trying to modify the "tryout_talent" mod using mod script hook. This mod requires adding a new function (getHiringTalents) to player.nut. Here is what I did:

::mods_hookNewObject("entity/tactical/player", function(o) {
o.getHiringTalents = function()
//copy the code from original mod
});

::mods_hookNewObjectOnce("ui/global/data_helper", function(o) {
o.convertEntityHireInformationToUIData = function( _entity )
//copy the code from original mod
});

However the games says: Script Error
the index 'getHiringTalents' does not exist

Could you tell me what's the problems in my code? Thanks a lot.
an anonymous Liaoran
RE: Question for the modding script hook 2019-02-14 07:18PM
A bit off topic, but the problem is that the 'player' class in the base game does not have a getHiringTalents function. If you're trying to add a new function, you need to use the <- syntax, as in o.getHiringTalents <- function() { ... }
2019-05-13 04:32PM
Adam,

Thank you for all the hard work on this!

What do you use to compress the modified scripts after editing and encrypting them again? I have not been able to get anything to work in the game. I even tried compressing a mod I made no changes to, just extracted the files and then compressed them again, and the mod won't work.

Any help is greatly appreciated!
an anonymous qubit
2019-05-21 10:21PM
Hi qubit, I just use the .zip file support built into Windows Explorer in Windows 7, but I'm sure any program that can generate a valid .zip file (e.g. 7-zip, Winzip, etc) would work. Perhaps the tool you are using is simply broken?
Is there an IDE for this language? 2019-05-27 06:33PM
I'm kind of tired to opening these files in notepad++ and and reading through text. its 21st century...
an anonymous Thomas
RE: Is there an IDE for this language? 2019-06-01 03:14PM
Hi, Thomas. There is one IDE that I'm aware of, but I haven't tried it. It's called Nutty. http://nutty.sourceforge.net/
2019-06-15 04:09PM
Due to MODs modifying and creating backgrounds, is it possible for you to share the program that scans scripts and automatically generates character backgrounds spreadsheet?
an anonymous Liaoran Cao
unpacking sprite sheets 2019-06-28 05:53AM
Ok maybe you can help me out if you are still around: I want to unpack the sprite sheets (or brushes if thats their name) so im typing into the cmd-window:
cd C:\Users\Besitzer\Desktop\bbros\bin
followed by:
bbrusher unpack C:\Users\Besitzer\Desktop\data_001\gfx\ui.brush
and its telling me that the "ui.brush" cant be found
im aiming at the ui.png sprite sheet - cant figure out why its not working even with the manual
an anonymous Eriktion
RE: unpacking sprite sheets 2019-07-01 08:45PM
Hi, Eriktion. The most likely reason it says ui.brush can't be found is because ui.brush does not exist in the gfx folder. It exists in the brushes folder. So try this instead:

bbrusher unpack C:\Users\Besitzer\Desktop\data_001\brushes\ui.brush
Modifying setStartValuesEx 2019-08-04 07:56AM
I tried to modify function setStartValuesEx in entity/tactical/player using the hook. What I did was just giving all mercs two traits. The problem is that the code works for all recruits in towns, but it does not work for bros created in events.

Do you have any idea what's the possible reason for such behavior? Thanks.

Here is how I used the hook:

local setStartValuesEx = function( _backgrounds, _addTraits = true )
{
//copy the original function here but set maxTraits = 2
}

::mods_hookClass("entity/tactical/player",
function(o) { ::mods_override(o, "setStartValuesEx", setStartValuesEx); });
an anonymous Liaoran Cao
RE: Modifying setStartValuesEx 2019-08-06 05:54PM
Hi, Liaoran. As far as I know, bros created in events often have their traits and stats and whatnot determined at least in part by the event logic. I would take a look at the logic for the particular event and see if you can trace how it works.
RE: RE: Modifying setStartValuesEx 2019-08-06 08:05PM
Thank you. I'm tracing the assasin event in noble war. The event is defined in scripts\events\bastard_assassin_event, and the code related to bros creation is listed below:

function start( _event )
{
local roster = this.World.getTemporaryRoster();
_event.m.Assassin = roster.create("scripts/entity/tactical/player");
_event.m.Assassin.setStartValuesEx([
"assassin_background"
]);
this.Characters.push(_event.m.Assassin.getImagePath());
}

It is actually pretty similiar to the code that generates bros in town, which is defined in scripts\entity\world\settlement.nut and listed as below:

while (maxRecruits > current.len())
{
local bro = roster.create("scripts/entity/tactical/player");
bro.setStartValuesEx(draftList);
current.push(bro);
}

Personally I can hardly see what's the difference between those two. Also, by adding a debug line to write some warnings in log, it seems that the script I made (in previous post) is actually not executed when the event is triggered.
an anonymous Liaoran Cao
possible bug on masscompile.bat? 2019-11-02 12:20AM
Hi, Thanks for making a great tool.
Recently, I found that the masscomplie.bat makes an error whenever I try to complie (the original 1.3.0.25 ver) event/dlc2/location/kraken_cult_enter_event.nut and event/dlc2/location/witchhut_destroyed_event.nut.

Could you check whether it is a bug or not?


Thanks!
an anonymous JH
RE: possible bug on masscompile.bat? 2019-11-12 12:49PM
Hi, JH. If you paste the error I could say better, but probably it is a bug in the Nutcracker compiler, which is known to be imperfect. You should not use masscompile.bat to recompile the entire script tree. You should only recompile the scripts that you've changed.
Permission 2020-01-01 07:32AM
Hello and thanks for sharing this great post with us. May I get your permission to share it on my website <a href="https://www.zettamods.com/games/battle-brothers">https://www.zettamods.com/games/battle-brothers</a>
an anonymous Emily
RE: Permission 2020-01-02 11:38PM
Hi, Emily. I don't mind if you put it or your website if there's a link back here so people can see updates.
Permission 2020-01-17 06:09PM
Hello,

Could you allow us to integrate your 'Weighted Talent' Mod to the legend Mod? I tried to make one myself but it turns out buggy and I cannot fix it.
an anonymous Liaoran Cao
RE: Permission 2020-01-22 07:44PM
Hi, Liaoran. Yes, go ahead.
Unpacking 2020-04-07 07:13AM
Hi Adam,

I'm running into an issue with trying to unpack sprites using bbrusher. Every time I try unpacking the entity_#.brush files I receive an error stating "ArgumentException - Parameter is not valid." I've tried unzipping the .brush file from the dat file, I've tried unpacking it from the dat file itself, I've tried specifying a directory to unpack to, and nothing seems to be working.

Any ideas on what might be going on? Thanks!
an anonymous Aric
RE: Unpacking 2020-04-09 01:17AM
Hi, Aric. Can you show the exact commands you've been running and their output, and describe where the files are when you run them?
Need help 2020-06-09 01:31PM
Hi Adam,

I'm running into an issue with a mod i made, i really don't know why this "Script Error the index 'isStacking' does not exist" kept appearing.

Maybe you could give me some advice to fix this.thanks!



an anonymous Hans
RE: Need help 2020-06-09 04:22PM
Hi, Hans. It sounds like you made a mistake in your code. If you send me an email with a copy of your code, perhaps I'll be able to see where the mistake is.
Video Tutorial 2020-08-20 01:19PM
Do you think you'll ever make a video tutorial detailing how to use the modkit?
an anonymous JD
Unpack Brush File 2020-08-20 11:34PM
Hello Adam, with the latest DLC release, i have tried to use your modkit to unpack and look into some brush files but, i kept facing error to unpack these brush files. The code that i used is:

C:\Users\Admin\Desktop\Modkit\bin>bbrusher unpack C:\Users\Admin\Desktop\Brushes\entity_6.brush

I receive an error stating "ArgumentException - Parameter is not valid." every time
Can you help me to solve this? Thanks!
an anonymous Hans
RE: Unpack Brush File 2020-08-22 10:01AM
Hi, Hans. I think you'll need to provide more information because it worked when I tried it:

D:\code\bbros>bbrusher unpack data\brushes\entity_6.brush
Opening gfx/entity_6.png...
entity\bodies\southern\bust_body_southern_01.png
entity\bodies\southern\bust_body_southern_01_damaged.png
entity\bodies\southern\bust_body_southern_01_dead.png
...

What is the structure of your files? Do you have the gfx\entity_6.png file in the appropriate place?
deserialize error when editing skill.nut 2020-08-30 11:24PM
Hello Adam,
Thank you for making such amazing tools.
I tried it and found it very useful for understanding this game.

But I hit a crash when I copied skills.nut to BBGame\data\scripts\skills folder, even remain it unchanged.
The crash dialog said "ID mismatch while deserializing script data."
How to solve it? Does it mean I cannot change some of scripts? I can change files in senarios and it works well.
This could be a very stupid question but I want to seek some light from you.
Thanks!
an anonymous Clyres
RE: deserialize error when editing skill.nut 2020-09-16 12:40AM
Hi, Clyres. I assume you're talking about skill.nut rather than skills.nut? Anyway, I tried it myself and did not see any crash. The game still worked for me after I copied that file, unchanged, into the data directory. So I can't say why you got a crash. Perhaps you are doing something differently. That said, you generally should use mod_hooks (https://www.nexusmods.com/battlebrothers/mods/42) rather than overwriting game files.
Still a great too, thx! 2020-09-28 10:55AM
Just wanted to say thank you for making these tools. BB has grown over the years and these tools continue to add mileage to a great game
an anonymous pandamonium
Reloading a mod 2020-11-03 09:17PM
Is it possible to reload a mod without restarting the game? Or are there other convenient ways of debugging?
an anonymous Alexander
RE: Reloading a mod 2020-11-12 02:02PM
Hi, Alexander. It is possible but difficult. Loading the game files happens only once, on startup, so any reloading of a mod would have to be implemented via parsing Squirrel script dynamically at runtime. There is no built-in debugger, at least in the release version of the game, so this is a kind of thing that would have to be added by the community (or an individual mod author), and nobody has done it.
2020-12-05 12:11PM
"unauthorizedaccessexception access to the path is denied" when i tryed to pack .brush files with bbrusher, i run console with administrator rights etc, with unpacking have zero problem, Adam help plz! <3
an anonymous Paul
decompilation errors 2021-01-23 12:12PM
Hello.
In the last version of BB (.47) I can't decompile cultist_origin_vs_old_gods_event, it always causes "Error: bad conversion" no matter how many times I re-run the decompiler.
In the past I had a similar issue with one of Legends files until it turned out that that file contained the following symbol: ’ - which looks like an apostrophe but isn't and as soon as it was replaced, the filed started decompiling properly. So maybe the issue with cultist_origin_vs_old_gods_event is similar.

Also I frequently have completely random "Error: bad conversion" errors: like, roughly speaking, I run the decompiler for 100 files 10 times and every time it bugs out 1-2-3 different files.
Is there any random code in the decompiler? like, some random bruteforcing or something? if so, it may have an error somewhere.

Anyway, that was just a minor bug report, and thank you for the modding tools!
an anonymous Mindeveler
RE: decompilation errors 2021-02-02 09:04PM
Hi, Mindeveler. The nutcracker decompiler is known to be imperfect. I didn't write it, but I did fix 10-15 bugs in it as part of developing the mod kit. Some bugs remain but it now works about 99+% of the time. If I get time, I can try to find the problem with cultist_origin_vs_old_gods_event.cnut but since I'm very busy recently I'm not sure when that'll be. I also started writing my own decompiler from scratch so I can replace that buggy nutcracker decompiler, but I haven't finished it.

That said, I don't think there is any random aspect to the decompiler. In all the other cases when I've seen people report such "random" decompilation failures it's been because they have messed up the encryption. It's possible to decrypt twice or encrypt twice or fail to decrypt and get things messed up.

Sorry that I don't have any immediate fixes for you.
-- Adam
RE: decompilation errors 2021-02-02 09:31PM
By the way, I don't think the cultist_origin_vs_old_gods_event.cnut script has ever decompiled successfully. It hasn't worked in the last 20 months at least, according to my records. Also, if you really want to read or edit the file, you can use my work-in-progress decompiler. It includes a pretty nice disassembler and assembler, and those work perfectly as far as I know. It's significantly more challenging to edit Squirrel VM assembly code compared to Squirrel script code, though. For example, instead of
function start(_event)
{
  this.Characters.push(_event.m.OldGods.getImagePath())
  this.Characters.push(_event.m.Cultist.getImagePath())
}
you'll be looking at code like
.function main.create.start#2 ; #2
.params this, _event
.literal "Characters" ; #0
.literal "push" ; #1
.literal "m" ; #2
.literal "OldGods" ; #3
.literal "getImagePath" ; #4
.literal "Cultist" ; #5
.stack 6

.locals this:0:18, _event:1:18 ; valid from 0 to 17
 GETK this.Characters -> @2 ; literal #0
 PREPCALLK this.Characters@2.push -> @2, @3 ; literal #1
 GETK _event.m -> @4 ; literal #2
 GETK _event.m@4.OldGods -> @4 ; literal #3
 PREPCALLK _event.m.OldGods@4.getImagePath -> @4, @5 ; literal #4
 CALL _event.m.OldGods.getImagePath@4(_event.m.OldGods@5) -> @4
 CALL this.Characters.push@2(this.Characters@3, @4)
 GETK this.Characters -> @2 ; literal #0
 PREPCALLK this.Characters@2.push -> @2, @3 ; literal #1
 GETK _event.m -> @4 ; literal #2
 GETK _event.m@4.Cultist -> @4 ; literal #5
 PREPCALLK _event.m.Cultist@4.getImagePath -> @4, @5 ; literal #4
 CALL _event.m.Cultist.getImagePath@4(_event.m.Cultist@5) -> @4
 CALL this.Characters.push@2(this.Characters@3, @4)
 RETURN
If you think you can deal with that and want to give it a try, let me know.
RE: decompilation errors 2021-02-03 10:33AM
Ahh, here's a solution for you, for cultist_origin_vs_old_gods_event.cnut. It does indeed contain a weird character, and if you replace it with a regular apostrophe then the file decompiles successfully. Search for "spools". The text is "... spools before you until it’s...".
RE: decompilation errors 2021-02-05 08:58AM
Hello.
Regarding random corrupted files - let me explain the situation again.
For example, I download the new Legends build, unpack it and run the following command: massdecompile.bat H:\legends_scripts\scripts\events
Say, the events folder has 200 files.
1st launch - files #10, #44 and #156 are broken, everything else decompiled properly.
2nd launch - files #27, #91 and #199 are broken, everything else decompiled properly (including those that failed to decompile on the first try).
3rd launch - files #37, #57, #87 and #107 are broken, everything else decompiled properly.
Etc.
Every time I rune massdecompile.bat on the exact same large enough folder, a few files inevitably break and every time it's completely random different files. And every time files that broke before decompile without issues (with the exception of permanently bugged files like cultist_origin_vs_old_gods_event.cnut, ofc).
So in the end I have to decompile the whole Legends archive in small portions - small enough to only trigger 1-2 broken files, then I search for all cnuts in this subfolder, delete them all except for the ones that still need to be decompiled to fix broken files, run the decompiler again and only then move on to the next subfolder.
Does no-one else have this issue?
The error that appears in the cmd for each broken file is the following: "File ~tmp.cnut had error the index "std_stream" does not exist".

As for that cultist file - thank you for the offer, but no, I'm neither good enough to understand assembly code nor do I really need that file. It was just a bug report in case you decide to take a look at it and patch the decompiler.
an anonymous Mindeveler
RE: decompilation errors 2021-02-06 02:04PM
Hi, Mindeveler. I've never seen massdecompile.bat fail with an error, let alone fail in such a random way as you describe. You're not using PowerShell, are you? (If so, use cmd.exe.)

As for the cultist file, in my previous message I did describe how it can be decompiled successfully, just in case you do want to look inside.
RE: decompilation errors 2021-02-06 03:32PM
That said, I've never used it with the Legends mod.
Brothers going to minus health 2021-04-16 05:04AM
Hi was just trying to update an origin mod for my own use and while I got it working now brothers dont die but rather remain standing on minus health and have a corpse model at same time. Just wondering if anyone else had come across this and knew what might be the cause? Only change i made was to change Tags to Flags for designating player character.
an anonymous jamie
IO and interaction with external program from Squirrel 2021-04-24 06:52AM
Hi Adam,
Thank you for the nice tools. You seem quite proficient at Squirrel scripting. I was trying to find a way to interact with other processes from Squirrel as it is in Battle Brothers.
Usually in Squirrel you have the file class to read/write to files. Also there is the system(cmd) function to start another process. Unfortunately, in Battle Brothers they seem to be missing.
Do you know any way for two-way communication with external processes?
an anonymous Sogartar
RE: IO and interaction with external program from Squirrel 2021-04-30 02:41PM
Hi Sogartar,

The Squirrel system in Battle Brothers is restricted. As you noticed, it is missing the I/O and process libraries, etc. I can think of a couple ways to achieve two-way communication with another process, but not nice ones.

First, modify the Battle Brothers executable to add new features. I imagine the executable doesn't change very often and pretty much all recent changes are implemented in Squirrel script, so an .exe patch might be stable for a while, but it's still a complicated task to create.

Second, establish a shared-memory communication channel based on a Squirrel array. For example, if you create an array and fill the first few elements with a signature that another process can find by scanning through the memory space, you can find the storage for that array. Then, the processes can communicate by reading and writing the elements of the array according to some protocol. That said, there are some dangers and/or difficulties, depending on whether Squirrel has a garbage collector that can move memory around. (If not, this could be pretty reliable.)
my response to programming jargon 2021-10-26 12:38PM
In the quiet words of the Virgin Mary, come again?
an anonymous Th
RE: my response to programming jargon 2021-11-02 09:49AM
Hi, Th. Thankfully, you don't need to read the programming stuff to use the mod kit. :-) The Steam guide for modding - https://steamcommunity.com/sharedfiles/filedetails/?id=1630767682 - may be a bit more accessible, if slightly outdated. That said, the mod kit is only for creating new mods. You don't need it to use existing mods. And if you want to create new mods, you're likely going to need to do some programming (unless you're just changing graphics, in which case you don't need this mod kit).
Thank you sooo much 2021-12-28 08:13AM
I'm super late to the party, but I wanted to thank you for this mod kit. BB is among my favorite games and when you released your mod tools in 2019 it really gave the game fresh life.
an anonymous Markus
2022-03-11 09:09PM
Master,i found that,
in your "masscompile.bat",
just one more "&" like:
sq -o "%%~dpnf.cnut" -c "%%f" && bbsq -e "%%~dpnf.cnut
but not:
sq -o "%%~dpnf.cnut" -c "%%f" & bbsq -e "%%~dpnf.cnut
then maybe it will run rightly...
i guess since i search some advice about "how to run order after another only when the first order run successfully" on the internet.
an anonymous bibi
ModKit on Gnu/Linux 2022-11-19 09:28PM
Hello Mr Milazzo

Amazing bit of kit! Thank you very much for your efforts.

I followed your instructions above to rewrite the batch scripts as bash scripts and was able to run everything nicely using wine on Gnu/Linux.
an anonymous JJ
RE: ModKit on Gnu/Linux 2022-11-21 11:06PM
Glad to hear it!
Thanks Adam 2023-04-20 11:34PM
Thanks Adam! Recently, I have localized the scripts of Battle Borothers. I have been using Nutcracker for some time, and I do found some bugs in decrypted .nut file. Looking forword to your own decompiler.
an anonymous fox
Pack Brush File 2023-11-18 10:08AM
Hello Adam,
Thank you for making such great tools.I'm sorry I only discovered this tool now.I just learned how to create mods. When I repackaged the brush file, the console reported an error of "unauthorizedaccessexception access to the path is denied". I tried to give all permissions to the folder in the security tab, but this error still occurred.

May I ask how to solve this problem? Thank you very much for your help.
RE: Pack Brush File 2023-11-21 11:24AM
Hi, Yun Liu. I think you're using it wrong. You can run "bbrusher" with no arguments to get a description of how to use it. When packing, the command line should look like "bbrusher pack A.brush dirPath" where A.brush is the name of the brush file you want to write, and dirPath is the path to the directory containing the sprites.xml file. If you mix up the arguments, e.g. by specifying the directory as the first argument, then it'll try to open the directory as a file, which will cause an access denied error.
repack brush file 2023-12-05 09:36AM
Hi Adam, really appreciate your tools! I have finally unpacked the brush file, and change some assets inside. Now, I would like to repack it. However, I followed the guidance but it only returns the usage msg... I cant find the brush/png file. Can you give me some guidance on this? Thx

C:\Nerv\Game\Battle Brothers v1.5.0.15\download\bbros\bin>bbrusher pack huoshou_2.brush C:\Nerv\Game\Battle Brothers v1.5.0.15\download\bbros\bin\huoshou_1
Usage: bbrusher unpack [<options>] <file.brush> [<outputDirectory>]
Unpacks all sprites from the image referenced by file.brush into the given
output directory. If the output directory is not specified, it will be named
based on the brush file and created in the current directory.

Usage: bbrusher pack [<options>] <file.brush> <inputDirectory>
Packs all PNG images from the input directory into a sprite sheet and
creates the file.brush file describing the sprite sheet. The input directory
must contain an appropriate sprites.xml metadata file describing the images.

Options:
--gfxPath <directory>
Specifies the name of the directory where the sprite sheet image should
loaded from or written to. If omitted, the parent of the directory
containing the brush file will be used.
an anonymous Jason
RE: repack brush file 2023-12-14 03:32PM
Hi, Jason. From the looks of your command-line I would guess that the problem is that you have spaces in your path but you aren't using quotation marks. You are trying to provide this argument:

C:\Nerv\Game\Battle Brothers v1.5.0.15\download\bbros\bin\huoshou_1

But because of the spaces, it is being interpreted as three arguments: "C:\Nerv\Game\Battle", "Brothers", and "v1.5.0.15\download\bbros\bin\huoshou_1".

In general, command-line arguments are separated with spaces. If you have an argument with spaces in it, that argument needs to be quoted to let the shell know that it is just one argument and not multiple arguments. (This is not specific to bbrusher, but is a general feature of command lines.)

In short, you need to write this:

bbrusher pack huoshou_2.brush "C:\Nerv\Game\Battle Brothers v1.5.0.15\download\bbros\bin\huoshou_1"

I hope that helps.
-- Adam
Brush files and sprites 2024-03-03 12:06AM
I've successfully unpacked and edited all .brush files I need. I only changed texture/colour of the vanilla filed, didn't add anything.

Do I pack them into XYZ.brush with "pack [<options>] <file.brush> <inputDirectory>"?

Will that create a huge entity_0.png sprite sheet in GFX folder or do I need to create/edit those manually?

Thanks

an anonymous Nikola
RE: Brush files and sprites 2024-03-06 04:29PM
Hi, Nikola. Yes, it should create the sprite sheet for you. It should place it in the parent of the directory containing the .brush file, by default, but this can be changed with the --gfxPath option.
pack brush 2024-05-15 06:22PM
Hi Adam, sorry for the trouble. I tried to open the bbrusher.exe then it asks me to install something, and i did. then I double click on it, it doesnt work. I then somehow uses Get-ChildItem ..\brushes | ForEach-Object {..\bin\bbrusher.exe unpack $_.FullName} to unpack the brushes to another location and it works. I modify the weapon and xml sheet. but the questions is how to i pack the file into a brush and speadsheet? i look the tutorial says bbrusher pack brush directory but where should I enter that? please help
an anonymous Jeff Wang
Brushes... again 2024-08-16 10:29AM
Looks like I'm not the only one struggling with this. Whether I use Jeff's powershell method above or this in cmd: bbrusher unpack <file>.brush

Both give the same error:
Opening gfx/<file>.png...
ERROR: ArgumentException - Parameter is not valid.
https://imgur.com/a/0V4ElvO

Tried official game files and mod brush files, same error. I'm giving up for now. Got the massdecompile.bat to work just fine so ¯\_(ツ)_/¯
an anonymous Blank
RE: Brushes... again 2024-08-20 02:24PM
Are you literally typing "<file>.brush"? You have to replace "<file>" with the name of the brush file.

Add a comment

Note: The information you enter (including your name and email address) will be displayed publicly.




Line breaks are converted to <br>'s, and all HTML tags except b, u, i, tt, and pre are filtered out.