Rhythm Heaven claims the spirit of yet another promising hacker, crushing his will with its labyrinthine code and impregnable graphics compression. We must persevere, to end its reign of terror once and for all.
Hi there,
I just played the last 6 hours with your game and I believe I found where the problem is and I contacted CUE (a member of this forum) because he is probably the one that can solve your problem the fastest.
As soon that i have a solution, i will tell you. Basically it should be only a matter of decompression/compression (I believe CUE tool would compress your file the right way but the decompressor has a problem. CT2 is no use because it's a "normal" compression but a special write mode.
If you has a solution to the decompressor, be ready to work with a bit less than 800 files (between graphics, tilemaps and palettes).
...
It's just that that game use things nobody else use in any games hehe.
Basically what i see as the problem is that the game uses BIOS decompression ID12 that is a LZ77 that is directly decompressed to the vram and to do this, he has some special writing methods.
Cue is ready to help but sadly he could not access the files i send to him to find out why his de-/compression tools didn't work.
For the technical mumbo jumbo (still assuming that i am right with my theory):
SWI 11h (GBA/NDS7/NDS9) - LZ77UnCompWram
SWI 12h (GBA/NDS7/NDS9) - LZ77UnCompVram (NDS: with Callback)
Expands LZ77-compressed data. The Wram function is faster, and writes in units of 8bits. For the Vram function the destination must be halfword aligned, data is written in units of 16bits.
If the size of the compressed data is not a multiple of 4, please adjust it as much as possible by padding with 0. Align the source address to a 4-Byte boundary.
r0 Source address, pointing to data as such:
Data header (32bit)
Bit 0-3 Reserved
Bit 4-7 Compressed type (must be 1 for LZ77)
Bit 8-31 Size of decompressed data
Repeat below. Each Flag Byte followed by eight Blocks.
Flag data (8bit)
Bit 0-7 Type Flags for next 8 Blocks, MSB first
Block Type 0 - Uncompressed - Copy 1 Byte from Source to Dest
Bit 0-7 One data byte to be copied to dest
Block Type 1 - Compressed - Copy N+3 Bytes from Dest-Disp-1 to Dest
Bit 0-3 Disp MSBs
Bit 4-7 Number of bytes to copy (minus 3)
Bit 8-15 Disp LSBs
r1 Destination address
r2 Callback parameter (NDS SWI 12h only, see Callback notes below)
r3 Callback structure (NDS SWI 12h only, see Callback notes below)
Return: No return value.
NDS Decompression Callbacks
On NDS7/NDS9, the SWI 12h, 13h, 15h functions are reading source data from callback functions (rather than directly from memory). The callback functions may read normal data from memory, or from other devices, such like directly from the gamepak bus, without storing the source data in memory. The downside is that the callback mechanism makes the function very slow, furthermore, NDS7/NDS9 SWI 12h, 13h, 15h are using THUMB code, and variables on stack, alltogether that makes the whole shit very-very-very slow.
r2 = user defined callback parameter (passed on to Open function)
r3 = pointer to callback structure
Callback structure (five 32bit pointers to callback functions)
Open_and_get_32bit (eg. LDR r0,[r0], get header)
Close (optional, 0=none)
Get_8bit (eg. LDRB r0,[r0])
Get_16bit (not used)
Get_32bit (used by Huffman only)
All functions may use ARM or THUMB code (indicated by address bit0). The current source address (r0) is passed to all callback functions. Additionally, the initial destination address (r1), and a user defined parameter (r2) are passed to the Open function. All functions have return values in r0. The Open function normally returns the first word (containing positive length and type), alternatively it may return a negative error code to abort/reject decompression. The Close function, if it is defined, should return zero (or any positive value), or a negative errorcode. The other functions return raw data, without errorcodes. The SWI returns the length of decompressed data, or the signed errorcode from the Open/Close functions.
This hack is being worked on chronologically: starting with the first game and moving a little bit forward each week. In addition to translating mini-games every week, we also translate all bonus content associated with that game. If we translate game #12, we also translate drum lesson #4 because you can unlock it by getting medals on 12 games.
/*----------------------------------------------------------------------------*/
#include "common.inc"
/*----------------------------------------------------------------------------*/
void Title(void);
void Decode(char *filename, int offset);
void DecodeNibble(int value, int color);
void UpdateTile(void);
/*----------------------------------------------------------------------------*/
int nTile, yTile, xTile;
/*----------------------------------------------------------------------------*/
int main(int argc, char **argv) {
Title();
Decode("Rhythm Tengoku (Japan).gba", 0x00D1AD4C);
printf("\nDone\n");
exit(EXIT_SUCCESS);
}
/*----------------------------------------------------------------------------*/
void Title(void) {
printf("Rhythm Tengoku v1.0 - GBA\n");
printf("Health screen decoder test\n");
printf("Copyright © CUE 2012\n");
printf("\n");
}
/*----------------------------------------------------------------------------*/
void Decode(char *filename, int offset) {
unsigned char *rom;
unsigned int count, idx1, idx2;
unsigned int colors, nbytes, code, nibble;
unsigned int i;
// load the rom, checks not added
rom = FileLoad(filename); // the only function defined in common.inc, use your own FILE READ function
// index 1 -> offset to count
// index 2 -> offset to encoded data
idx1 = *(unsigned int *)(rom + offset + 4) - 0x08000000;
idx2 = *(unsigned int *)(rom + offset) - 0x08000000;
idx2 = *(unsigned int *)(rom + idx2) - 0x08000000;
// init tile data
nTile = xTile = yTile = 0;
// encoded data:
// - 1st byte -> foreground and background colors (palette colors)
// - 2nd byte -> number of encoded words - 1
// - 3rd byte -> words of encoded data
// count:
// - 1st value -> copy 'n' nibbles, zero to end
// - 2nd value -> repeat 'n' times the latest nibble
for (count = 0; count != -1; ) {
colors = rom[idx2++];
nbytes = (rom[idx2++] + 1) >= 4;
if (!count) {
count = rom[idx1++];
//printf("[0:%02X]", count);
}
DecodeNibble(nibble, colors);
count--;
if (!count) {
count = rom[idx1++];
//printf("[1:%02X]", count);
if (!count) { count = -1; break; }
while (count--) DecodeNibble(nibble, colors);
count = 0;
}
}
}
// fill the tile row
// 2 more bytes needed
// each byte has the real color number, 2 pixels/byte
if (yTile) {
printf("****"); // the 4 colors
for (i = 0; i < 2; i++) {
code = rom[idx2++];
}
UpdateTile();
count--;
}
else if (!xTile) {
idx2+=4;
UpdateTile();
count--;
UpdateTile();
count--;
}
//printf("%08X %08X\n", idx1, idx2);
break; // stop 1st block
}
free(rom);
}
/*----------------------------------------------------------------------------*/
void DecodeNibble(int value, int colors) {
unsigned char symbol[] = ".#";
unsigned int i;
// 4 bits/nibble, each bit is a color:
// - 0 -> color >> 4
// - 1 -> color & 0xF
// ".#" is for test purposes
for (i = 0; i < 4; i++) {
printf("%c", symbol[value & 1]);
value >>= 1;
}
UpdateTile();
}
/*----------------------------------------------------------------------------*/
void UpdateTile(void) {
// 1 tile = 8 rows of 8 columns, 8x8 pixels
// 4 bits each nible
yTile += 4;
if (yTile == 8) {
if (!xTile) printf(" tile %03X", nTile);
printf("\n");
yTile = 0;
if (++xTile == 8) {
printf("\n");
xTile = 0;
nTile++;
}
}
}
/*----------------------------------------------------------------------------*/
// eof
Rhythm Tengoku v1.0 - GBA
Health screen decoder test
Copyright (C) CUE 2012
........ tile 000
........
........
#.......
......##
....##..
...#....
....##..
........ tile 001
........
.#....#.
#...####
......#.
......#.
.....#..
.....#..
........ tile 002
........
..#.#...
#.#.#.##
........
####....
.......#
......#.
........ tile 003
........
..#.....
######..
...#....
...#....
#####...
....#...
,..