Skip to content

Instantly share code, notes, and snippets.

@barncastle
Last active November 19, 2023 04:11
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);
}
}
@Talafhah1
Copy link

The Decrypt static method is called 4 times, once for each .swz file stream; its output is a string[] that has all the files included in the .swz as a string.
So, simply use a foreach loop on the elements of the output of Decrypt and run the filename block, then use the file_name string in the file stream writer.

@yachecker
Copy link

i cannot find anything
image
here 0_O

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