Skip to content

Instantly share code, notes, and snippets.

@barncastle
Last active September 6, 2025 12:27
Show Gist options
  • Save barncastle/a21b62df945445b38daf91ede021a3ec to your computer and use it in GitHub Desktop.
Save barncastle/a21b62df945445b38daf91ede021a3ec to your computer and use it in GitHub Desktop.
Code for decrypting Blue Mammoth Games' Brawlhalla's SWZ files
// WEll512 implementation - https://gist.github.com/barncastle/0fb2279bdc337d2a7d951e1bd2e3c0df
using Ionic.Zlib;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
static class BrawlhallaSWZ
{
public static string[] Decrypt(Stream input, uint globalKey)
{
var checksum = ReadUInt32BE(input);
var seed = ReadUInt32BE(input);
// initialise WELL512
var rand = new WELL512(seed ^ globalKey);
// compute and compare the header checksum
// this also mixes the WELL512 state so is required
var hash = 0x2DF4A1CDu;
var hash_rounds = globalKey % 0x1F + 5;
for (var i = 0; i < hash_rounds; i++)
hash ^= rand.NextUInt();
Debug.Assert(hash == checksum);
// decrypt each string object
var results = new List<string>();
while (input.Position != input.Length)
{
if (ReadStringEntry(input, rand, out var stringEntry))
results.Add(stringEntry);
}
return results.ToArray();
}
public static byte[] Encrypt(uint seed, uint globalKey, params string[] stringEntries)
{
// initialise WELL512
var rand = new WELL512(seed ^ globalKey);
// compute the header checksum
var hash = 0x2DF4A1CDu;
var hash_rounds = globalKey % 0x1F + 5;
for (var i = 0; i < hash_rounds; i++)
hash ^= rand.NextUInt();
using var ms = new MemoryStream(0x1000);
WriteUInt32BE(ms, hash);
WriteUInt32BE(ms, seed);
foreach (var entry in stringEntries)
{
var stringBytes = Encoding.UTF8.GetBytes(entry);
WriteStringEntry(stringBytes, rand, ms);
}
return ms.ToArray();
}
private static bool ReadStringEntry(Stream input, WELL512 rand, out string result)
{
// read the object header XOR'ing the size fields
var compressedSize = ReadUInt32BE(input) ^ rand.NextUInt();
var decompressedSize = ReadUInt32BE(input) ^ rand.NextUInt();
var checksum = ReadUInt32BE(input);
if (compressedSize + input.Position > input.Length)
{
result = null;
return false;
}
// read the compressed data
var buffer = new byte[compressedSize];
input.Read(buffer);
// again required even if not
// validating the checksum
var hash = rand.NextUInt();
for (var i = 0; i < compressedSize; i++)
{
// decode the byte
var shift = i & 0xF;
buffer[i] ^= (byte)(((0xFFu << shift) & rand.NextUInt()) >> shift);
// update the local checksum
hash = buffer[i] ^ RotateRight(hash, i % 7 + 1);
}
Debug.Assert(checksum == hash);
// zlib decompress
var decompressedData = ZlibStream.UncompressBuffer(buffer);
result = Encoding.UTF8.GetString(decompressedData);
return true;
}
private static void WriteStringEntry(byte[] input, WELL512 rand, Stream output)
{
// zlib compress
var compressedInput = ZlibStream.CompressBuffer(input);
// calculate the field values
var compressedSize = (uint)compressedInput.Length ^ rand.NextUInt();
var decompressedSize = (uint)input.Length ^ rand.NextUInt();
// create the checksum
var checksum = rand.NextUInt();
for (var i = 0; i < compressedInput.Length; i++)
{
// update the checksum
checksum = compressedInput[i] ^ RotateRight(checksum, i % 7 + 1);
// encode the byte
var shift = i & 0xF;
compressedInput[i] ^= (byte)(((0xFFu << shift) & rand.NextUInt()) >> shift);
}
// write the fields and data
WriteUInt32BE(output, compressedSize);
WriteUInt32BE(output, decompressedSize);
WriteUInt32BE(output, checksum);
output.Write(compressedInput);
}
private static uint RotateRight(uint v, int bits)
{
return (v >> bits) | (v << (32 - bits));
}
private static uint ReadUInt32BE(Stream stream)
{
var buffer = new byte[4];
stream.Read(buffer);
return (uint)(buffer[3] | (buffer[2] << 8) | (buffer[1] << 16) | (buffer[0] << 24));
}
private static void WriteUInt32BE(Stream stream, uint value)
{
var buffer = new byte[4]
{
(byte)((value >> 24) & 0xFF),
(byte)((value >> 16) & 0xFF),
(byte)((value >> 08) & 0xFF),
(byte)((value >> 00) & 0xFF)
};
stream.Write(buffer);
}
}
@unsadddd
Copy link

unsadddd commented Sep 6, 2025

Ive gotten my game files dumped but theres a huge range of game files, from Game0 to Game69, how can I find the file and text i need which edits for example legends in game that the user owns, or coins, etc.

@anto123arci
Copy link

@unsadddd you cannot do that. Legends, coins and cosmetics are tied to your account, handled server side. Moreover, if you modify the game files you won't be able to play online or connect to the servers at all.

@CrossyChainsaw
Copy link

CrossyChainsaw commented Sep 6, 2025

@anto123arci what about unlocking legends / stances / skins in couch party, that should be somewhere local right? (I am not encouraging this)

it's funny how you still have to play a ton to unlock all legends offline

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment