Users browsing this thread:
Virtual Boy Wario Land ripping project, step one
#1
[Image: 5pK85.png]

Yeeeaaah dunno if this is allowed but bear with me, aight?

Virtual Boy Wario Land has a rich set of sprites, and though Virtual Boy emulation has been around for several years now, the amount of rips from the game are few at most. There are a variety of valid reasons why this is, though:
  • The graphics are compressed. Most of them are compressed with a variation of RLE, to which Previous wrote an experimental decompressor already. It isn't perfect though, and the game has another, yet-unknown compression method.
  • The sprites are layered. I'd call it the "Yoshi's Island problem", as the tiles look fine and are available easily, but layering them exactly as the game does is far trickier. And nobody really wants "compile your own sprite"-sheets.
So naturally, if we want to rip sprites from the game, overcoming the two problems above is essential.

The first problem, the graphics being compressed, is easy to bypass, even without a dedicated decompressor. The most actively developed Virtual Boy emulator, VBJin (Or, for Mac and Linux users, Mednafen) uses .mc# as its save state format. Opening an mc# in Tile Molester and heading to 0x77821 (2bpp linear, reverse-order) will show you two full graphic pages. These are the common room elements. These graphics are 'always' in the RAM and look like this. I'll get back on these later.

In 0x79821, two banks after the CRE, are the level elements. These are tiles that the game loads to RAM immediately after a level is entered.
These are also the tiles that I'm going to primarily elaborate on.

These are stage 1's level elements. I'm Dividing them into a $0Fx$0F grid for easier viewing:
[Image: is2DN.png]

So, there's the tiles. A lazy man would arrange them a bit, save and submit. Although that would be better than nothing, it's better to go for the gold. In the ROM, at 0x1BFA9 there's a byte array that essentially contains the exact instructions of how all of the sprite is assembled. Take a good look at the bytes. I've formatted them into 4 bytes per row.
Code:
E0 02 90 02
D8 02 80 02
E8 04 A0 02
F4 04 97 02
F4 FC 96 02
F8 04 A3 02
F8 FC A2 02
F8 F4 A1 02
F0 04 93 02
F0 FC 92 02
F0 F4 91 02
E8 04 83 02
E8 FC 82 02
E8 F4 81 02
E0 F8 90 22
D8 F8 80 22
E8 F6 A0 22
FF FF FF FF


DC 02 90 02
D4 02 80 02
E4 04 A0 02
F0 04 97 02
F0 FC 96 02
F4 04 A6 02
F4 FC A5 02
F4 F4 A4 02
FC 04 87 02
FC FC 86 02
EC FC 95 02
EC F4 94 02
E4 FC 85 02
E4 F4 84 02
EC 04 93 02
E4 04 83 02
DC F8 90 22
D4 F8 80 22
E4 F6 A0 22
FF FF FF FF


DC 02 90 02
D4 02 80 02
E4 04 A0 02
F0 04 97 02
F0 FC 96 02
F4 04 A6 02
F4 FC A5 02
F4 F4 A4 02
EC FC 92 02
EC F4 91 02
E4 FC 82 02
E4 F4 81 02
FC 04 87 02
FC FC 86 02
EC 04 93 02
E4 04 83 02
DC F8 90 22
D4 F8 80 22
E4 F6 A0 22
FF FF FF FF


E0 01 90 02
D8 01 80 02
E8 03 A0 02
F4 03 97 02
F4 FB 96 02
F8 04 A3 02
F8 FC A2 02
F8 F4 A1 02
F0 04 93 02
F0 FC 92 02
F0 F4 91 02
E8 04 83 02
E8 FC 82 02
E8 F4 81 02
E0 F9 90 22
D8 F9 80 22
E8 F7 A0 22
FF FF FF FF


DC 01 90 02
D4 01 80 02
E4 03 A0 02
F0 03 97 02
F0 FB 96 02
F4 04 A6 02
F4 FC A5 02
F4 F4 A4 02
FC 04 87 02
FC FC 86 02
EC FC 95 02
EC F4 94 02
E4 FC 85 02
E4 F4 84 02
EC 04 93 02
E4 04 83 02
DC F9 90 22
D4 F9 80 22
E4 F7 A0 22
FF FF FF FF


DC 01 90 02
D4 01 80 02
E4 03 A0 02
F0 03 97 02
F0 FB 96 02
F4 04 A6 02
F4 FC A5 02
F4 F4 A4 02
EC FC 92 02
EC F4 91 02
E4 FC 82 02
E4 F4 81 02
FC 04 87 02
FC FC 86 02
EC 04 93 02
E4 04 83 02
DC F9 90 22
D4 F9 80 22
E4 F7 A0 22
FF FF FF FF


E8 FD AC 02
E8 F5 9C 02
E9 ED 8C 02
F8 06 BD 02
F0 06 BC 02
F0 10 A7 02
F8 08 AA 02
F8 00 A9 02
F8 F8 A8 02
F0 08 9A 02
F0 00 99 02
F0 F8 98 02
E8 08 8A 02
E8 00 89 02
E8 F8 88 02
F0 FC AC 02
F0 F4 9C 02
F1 EC 8C 02
FF FF FF FF


E8 FD AC 02
E8 F5 9C 02
E9 ED 8C 02
F7 06 BD 02
EF 06 BC 02
F8 0B AB 02
F0 0B 9B 02
E8 0B 8B 02
F8 08 AA 02
F8 00 A9 02
F8 F8 A8 02
F0 08 9A 02
F0 00 99 02
F0 F8 98 02
E8 08 8A 02
E8 00 89 02
E8 F8 88 02
F0 FC AC 02
F0 F4 9C 02
F1 EC 8C 02
FF FF FF FF


E8 FC AC 02
E8 F4 9C 02
E9 EC 8C 02
F8 05 BD 02
F0 05 BC 02
F0 10 A7 02
F8 08 AA 02
F8 00 A9 02
F8 F8 A8 02
F0 08 9A 02
F0 00 99 02
F0 F8 98 02
E8 08 8A 02
E8 00 89 02
E8 F8 88 02
F0 FD AC 02
F0 F5 9C 02
F1 ED 8C 02
FF FF FF FF


E8 FC AC 02
E8 F4 9C 02
E9 EC 8C 02
F7 05 BD 02
EF 05 BC 02
F8 0B AB 02
F0 0B 9B 02
E8 0B 8B 02
F8 08 AA 02
F8 00 A9 02
F8 F8 A8 02
F0 08 9A 02
F0 00 99 02
F0 F8 98 02
E8 08 8A 02
E8 00 89 02
E8 F8 88 02
F0 FD AC 02
F0 F5 9C 02
F1 ED 8C 02
FF FF FF FF


F8 FD B3 02
F8 F5 B2 02
F7 ED B1 02
EC 06 BD 12
F4 06 BC 12
F0 07 9F 02
F3 0F B0 02
F8 07 AF 02
F8 FF AE 02
F8 F7 AD 02
F0 FF 9E 02
F0 F7 9D 02
E8 07 8F 02
E8 FF 8E 02
E8 F7 8D 02
EF FC B3 02
EF F4 B2 02
EE EC B1 02
FF FF FF FF


F8 FD B3 02
F8 F5 B2 02
F7 ED B1 02
EB 06 BD 12
F3 06 BC 12
F8 0F BB 02
F8 07 BA 02
F0 0F B9 02
F0 07 B8 02
F0 FF B7 02
E8 0F B6 02
E8 07 B5 02
E8 FF B4 02
F8 FF AE 02
F8 F7 AD 02
F0 F7 9D 02
E8 F7 8D 02
EF FC B3 02
EF F4 B2 02
EE EC B1 02
FF FF FF FF


F8 FC B3 02
F8 F4 B2 02
F7 EC B1 02
EC 05 BD 12
F4 05 BC 12
F0 07 9F 02
F3 0F B0 02
F8 07 AF 02
F8 FF AE 02
F8 F7 AD 02
F0 FF 9E 02
F0 F7 9D 02
E8 07 8F 02
E8 FF 8E 02
E8 F7 8D 02
EF FD B3 02
EF F5 B2 02
EE ED B1 02
FF FF FF FF


F8 FC B3 02
F8 F4 B2 02
F7 EC B1 02
EB 05 BD 12
F3 05 BC 12
F8 0F BB 02
F8 07 BA 02
F0 0F B9 02
F0 07 B8 02
F0 FF B7 02
E8 0F B6 02
E8 07 B5 02
E8 FF B4 02
F8 FF AE 02
F8 F7 AD 02
F0 F7 9D 02
E8 F7 8D 02
EF FD B3 02
EF F5 B2 02
EE ED B1 02
FF FF FF FF FF FF FF
  • The first byte tells us the y coordinate of the tile
  • The second byte tells us the x coordinate of the tile
  • The third byte tells us the tile in question
  • The fourth byte tells us the orientation of the tile. Read bitwise as bbhv bbbb
    • h = horizontal flip
    • v = vertical flip
    • b = bank related. No real use and could be ignored (for now?)
  • FF FF FF FF means "the assembly for this single sprite is done"
  • FF FF FF FF FF FF FF means "the composition for this character's each sprite is done"

So let's see the grid:
[Image: 42cWj.png]
After some trial and error, I came to the conclusion that this kind of coordinate system is used. So let's assemble our first sprite by reading the instructions in order (this is crucial because the layering of the tiles depends on it. The horn in the back is printed last, because it's the sprite 'most behind')
Click here to download the instructional GIF. If you can, watch it frame-by-frame. It might go faster than you can keep up with of what's going on (I would've made a html slideshow with "next step" etc. buttons but I don't know how).

As you can see in the GIF, I pick up a tile, set its Y position (byte 1), then set its x position (byte 2), then elaborate what the tile is (byte 3), and finally go through the property byte to see if it's flipped (byte 4).
Then I read the next four bytes with the same idea. Repeat until FF FF FF FF. Save complete sprite as "sprite1-frame1", start reading bytes again, repeat, repeat, repeat until FF FF FF FF FF FF FF. Sheet done.

Well, not exactly. That's what I would do. But as you saw, the assembly is pretty tedious. Making that GIF alone took a nice chunk of time. Doing this by hand is absolutely out of the question, so I come forth with a question — could this process be automated by a program?
Of course it would. Being unable to program, though, this is as far as I can help here. If anyone has any interest in writing such a sheet-compiler, we could go forth towards step two.

"Step two? So step one is basically you asking for someone to write a program to do all the work so you can export sheets and claim as yours?!"

Not exactly. Step one here is pretty much only a theoretical, planning-stage type manifest. Even if the program is done, it will still need to know the assembly instructions and separate save states for each sprite set, and these are in no way linked—there is no pointer table in the ROM that tells where the instructions for each graphics page's sprites are. Most instructions I've found are directly after the respective compressed graphics, but, for example, instructions for the CRE are after the instructions of some random graphics with no indicator of "hey, we're not defining that guy's sprites anymore" anywhere inbetween.

The only clues we really have are that all(?) instruction sets begin with a 00 00 00 00 00 00 00 00-header and continue like I explained above until FF FF FF FF FF FF FF is met. That, and the fact that most instructions are after the compressed graphics.

I can single-handedly hunt down every instruction set in the ROM and make save states even, if enough people are interested. What I really want to know here is that is anyone interested in programming this essential tool, and if yes, once it's done, is any of you guys up for splitting the burden of "try this instruction set with that graphics file/save state until it matches" with me? Or something, I dunno.
Once there was a way to get back homeward
Thanked by: Garamonde, Gaia
#2
I'm not a technical guy, but if anyone can make this happen it's you,. Maybe PM Barubary?
[Image: b1.php?u=39480955]
Quote:You had wasted MY LIFE... waiting for just a goddamn bunnelby model.
-The prestigious Farlavor
Thanked by: Raccoon Sam
#3
Heh



I guess I could give it a go. Doesn't sound too complicated. Although it'd mean messing around with Java a lot Tongue
BufferedImage and .png saving look fairly simple at least.

However, I'd need a few example files (savestates mostly) and some information on the palette (as in, the four colours and their colour index (00 to 11)).

I'll see if I can find a rom, too. I guess it would be convenient if the program could just load the rom file, accept an offset from the user and read the assembly instructions from there.
Thanked by: Garamonde
#4
Oh man...I TRIED to actually rip this the old way, and the emulators were all so sped-up no matter what I did I couldn't manage to grab everything from what I wanted...then I got to the final boss and the sucker was partially transparent, making it even more impossible. I'm really glad someone is working on this. If you work anything out please let me know, as I'd like to help.
Thanked by:
#5
@Previous: Thanks for your interest! And yeah, the 'open ROM & choose offset'-approach sounds plausible instead of separate "instruction files". I'll contact you later today if you need the ROM/save states.
Can't really comment on the color index, but I know VB can display 4 colors per channel. Really it wouldn't matter too much, since whether they're red, blue or grayscale, they can always be recolored.

@Carpaccio: Haha, yeah. Not to mention none of the emulators (back then, at least) support layer disabling, so even if you do manage to get a screenshot, you'd have to "cut" it from the background. Also, I loved the final boss!
Once there was a way to get back homeward
Thanked by:
#6
I've got the Rom and VBjin. However, the offsets in its savestates are different, heh.

About the colours, well, I just thought you may have a preference or something. For now, I just took the reds from your first image up there, but I may end up choosing my own colours.
Regarding the tiles, "(2bpp linear, reverse-order)" doesn't seem to be correct, they look pretty much TILED in my savestate... so I had to rewrite the tile loading code Cry
Assembly instructions (I call them "blueprints" okay?) aren't read or processed yet, I only managed to get tile loading done last night Wink

I assume you want sheet output rather than each frame in a seperate image? As for the canvas size, I guess I'll track the maximum and minimum positions during assembling and cut the output down to that instead of using 256x256 images. Concerning the "lastz instruction is drawn at the back" thing I'll see if Java has a stack class ready (would be surprised if not).

Oh, would you prefer transparent images or opaque colour?
Thanked by:
#7
Fantastic. Also, that's strange. Maybe the VBJin core differs from the original mednafen so much that the save state format is that different. Can you post a screenshot of how you see the tiles? I'm not sure what you mean. Also, what's the offset in those save states?
And for sheet output... I'm not sure. Of course sheet output would be better, spriting-wise, but the blueprint specification is not perfect. I still don't know what the bank bits do completely, so identifying the problems which we'll run into as we go for >1 bank sprites will be easy if I can observe single sprites. But then again, I can do that in sheets too I guess. To be honest, whichever is easier to implement goes Smile

Transparency, I wouldn't go for it just yet. Just use green or something.

Smile
Once there was a way to get back homeward
Thanked by:
#8
[Image: 1DNcB.png]

(Final program will most likely look nothing like that)

Reading tile by tile, 2bpp little endian. Offset for these is #8EF34 (your "common room elements" from 0x77821 are at #8CF34).

I plan on making an in-app preview of assembled sprites so you don't have to save them and open the result images externally since you're going to do a lot of trial and error matching.

About your banks, well, that, the bbhvbbbb is probably more of an uuhvubbb with u being whatever (zero Tongue) and bb being another graphic index. The full graphics part is 128x1024 pixels, which makes 8 128x128 chunks with 256 tiles each. So if bbb is 002 as in your example, the tile offset is withing the third of these chunks.
I'll implement it that way and we'll see if it works, huh.
Thanked by: Ton, Garamonde
#9
Wow, great!! Can't wait to see what happens next Smile
Also, good job figuring out the bank bits. Smile
Once there was a way to get back homeward
Thanked by:
#10
What happens next is that I start raging out because NetBeans' GUI designer is awful
I like the precision I have in Delphi and C# was usable, too, but here, everything is weird and has its own mind and the running program will look completely different anyways. So don't expect anything nice-looking Cry
Let's see what I can do with the blueprints.
Thanked by:
#11
Well, to be honest, the interface doesn't really matter as long as it does what it needs to do. GGD is a great example—horrid interface, fantastic tool.
Java and C# would be cross-platform if macfags like me and linuxes want to join in, but I'm certain Delphi works in WINE too.
Once there was a way to get back homeward
Thanked by:
#12
[Image: jfCLv.png]

Saving is not implemented yet and I still need to make some nicer colours and an icon, but this is about it. Tiles on the left with a scrollbar, sprite in the center, some textbox on the bottom and buttons at the top. The "Load ..." buttons let you select a file, "reload" will reload the respective file (to be used after changing the offset or even the file itself what do I know). When loading a file, data will be read starting from the currently set offset, so you'd better set that before loading (or you hit "Reload" afterwards).
#13
[Image: Q2XOR.png]

Here it is for you to play around with and see what does work and what does not and all that.
Thanked by: Garamonde, Carpaccio
#14
Wow, looks astounding!! But.. I can't use it. It says this when I open it:
[0x0-0x84084].com.apple.JarLauncher[906] Exception in thread "main" java.lang.NoClassDefFoundError: mainJFrame (wrong name: vbwlsa/mainJFrame)

I'm running Mac OS X 10.6.8
Once there was a way to get back homeward
Thanked by: Previous
#15
It runs fine on my Windows 7 Tongue

OS should not matter, it's Java - the Java versiound should be more interesting.

I checked, it also does not run on my linux machine, but that doesn't really help since I already struggled to get any Java on it at all (a few packet sources weren't responding, very nice).

I'll look into it later, but I really have no clue. I made it in NetBeans after all.
Thanked by:


Forum Jump: