GUXT modding information

Aug 19, 2016 at 6:58 AM
In my body, in my head
Forum Moderator
"Life begins and ends with Nu."
Join Date: Aug 28, 2009
Location: The Purple Zone
Posts: 5998
Hey there
As you may or may not know, GUXT is the top-down shoot them up game made by Pixel in 2007. So far, to my knowledge, nobody has ever managed to mod it. But I don't think it should be too hard - all the data files are right there, in Pixel's signature style. So I might pick it apart and try to document what I find

First off is .pximg files. Now,at this point Pixel is still obfuscating his data but he's got some new tricks up his sleeve. Trying to open the image as a bitmap reveals a scrambled mess! Each row in the scrambled image can be passed through a hashing function to give its position in the real image. I've included a java implementation of the function below. Forgive the clumsiness at parts but Java doesn't play well with unsigned types.

Code:
public class Decoder {

	int img_height;
	int[] line_indexes;
	int hash_a;
	int hash_b;

	public Decoder(int h, int a, int b) {
		hash_a = a;
		hash_b = b;
		img_height = h;
		line_indexes = new int[img_height];
		Arrays.fill(line_indexes, -1);

		int current_line = 0;
		int cycle_count;
		for (int i = 0; i < img_height; i++) {
			cycle_count = (shuffle() >> 8) & 0xFF;
			if (cycle_count == 0) {
				cycle_count = 1;
			}
			do {
				current_line = (current_line + 1) % img_height;
				if (line_indexes[current_line] == -1)
					--cycle_count;
			} while (cycle_count != 0);

			line_indexes[current_line] = i;
		}

		System.out.println(Arrays.toString(line_indexes));
	}

	private int shuffle() {
		int tmp = (hash_a + hash_b) & 0xFFFF;
		byte low = (byte) tmp;
		byte high = (byte) (tmp >>> 8);
		short result = (short) (low << 8);
		result |= high & 0xFF;
		hash_b = hash_a;
		hash_a = result;

		return result;
	}

	public static void main(String[] args) {
		new Decoder(16, 0x4444, 0x8888);
	}
}

The output of this gives the lines of the scrambled image in the order they appear in the unscrambled image. e.g.
[9, 6, 5, 12, 11, 7, 4, 10, 2, 13, 14, 3, 0, 1, 15, 8]
so the 0th row in the unscrambled image is the 9th row in the scrambled one.

Now with that I've had access to all of Guxt's image assets, and one file in particular stands out to me
diph.php

I believe these are the icons used in pixel's own stage editor. Conveniently, they're probably also in the order of each unit's Entity ID. So this should help decode those a lot.
 
Aug 19, 2016 at 7:04 AM
In my body, in my head
Forum Moderator
"Life begins and ends with Nu."
Join Date: Aug 28, 2009
Location: The Purple Zone
Posts: 5998
they're ptnoise files
download ptnoise from the tribute site, it comes with pxtone
 
Aug 19, 2016 at 9:01 AM
Bonds that separate us
Forum Administrator
"Life begins and ends with Nu."
Join Date: Aug 20, 2006
Location:
Posts: 2846
Age: 33
Maybe I should casually mention things I'd like in chat more often
 
Aug 19, 2016 at 12:50 PM
Um... Chosen One? Yeah that'll work. : P
"Big Joe Tire and Battery Restaurant! Opening Soon! Eat at Big Joes!"
Join Date: Oct 7, 2013
Location: India
Posts: 499
Do you mind explaining what the algorithm is that was used to encode the bitmaps? Or is it more complicated than that? I am curious, but I don't know programming, so I can't figure it out from your code.
 
Aug 19, 2016 at 2:39 PM
Administrator
Forum Administrator
"Life begins and ends with Nu."
Join Date: Jul 15, 2007
Location: Australia
Posts: 6211
Age: 38
@Noxid: I am going to attempt to document your shuffle() function for everyone's sanity.

hash_a and hash_b start as 0x4444 and 0x8888 respectively (the 0x means that the number is in hexadecimal).

Now this line:
int tmp = (hash_a + hash_b) & 0xFFFF;

..adds hash_a and hash_b together. The "& 0xFFFF" in this case effectively means "modulus 0xFFFF". To put it simply if the result of "hash_a + hash_b" was 0x24FFF then it would become 0x4FFF. The result is saved in a variable called "tmp".

This:
byte low = (byte) tmp;
byte high = (byte) (tmp >>> 8);

..splits "tmp" into two appropriately named single byte variables.

This:
short result = (short) (low << 8);
result |= high & 0xFF;

..joins the single byte variables back together again as a two-byte variable (named "result") in the opposite order (high becomes low and vice versa).

The rest should be obvious:
hash_b = hash_a;
hash_a = result;

..hash_a is the new hash_b, and result becomes hash_a for the next time the function is called.

And "result" is returned:
return result;

Now I'll also note a line from the decoder while I'm here:
cycle_count = (shuffle() >> 8) & 0xFF;

..this takes the high byte from "result" (the return value for the shuffle function) and saves in the variable "cycle_count".

Do you mind explaining what the algorithm is that was used to encode the bitmaps? Or is it more complicated than that? I am curious, but I don't know programming, so I can't figure it out from your code.
Each row of pixels in the image is stored in a specific wrong order in a pximg file, the decoder Noxid posted gives you a printout of the original vs pximg row orders, which can be used for both decoding and encoding purposes, but it doesn't actually apply it to the image in either capacity.
 
Last edited:
Aug 19, 2016 at 3:37 PM
In my body, in my head
Forum Moderator
"Life begins and ends with Nu."
Join Date: Aug 28, 2009
Location: The Purple Zone
Posts: 5998
Yep, that looks about right.
I'll be honest, I mostly reconstructed the function from asm so it isn't the cleanest thing ever.

I purposefully left out the implementation detail of how i applied it to an image, because it's mostly just boilerplate image io operations.

Anyway, today I might try and see what sense I can make of pxmap
 
Aug 19, 2016 at 11:58 PM
Administrator
Forum Administrator
"Life begins and ends with Nu."
Join Date: Jul 15, 2007
Location: Australia
Posts: 6211
Age: 38
I purposefully left out the implementation detail of how i applied it to an image, because it's mostly just boilerplate image io operations.
Any plans on expanding it to a full encoder/decoder for us people who don't write java?
 
Aug 22, 2016 at 3:54 AM
The "C" in "college" is for "crippling debt".
Bobomb says: "I need a hug!"
Join Date: Nov 12, 2014
Location: East Coast America
Posts: 759
Age: 23
This is very intriguing. Pixel must hate us.

Oh hell yeah! Mind providing a link to all the converted bitmap files?
Also, what about the .ptnoise files, what're they?

Yeah, the .ptnoise files are there because the entire sound system in GUXT is done in Pxtone, if I'm not mistaken.
 
Top