There have been many utilities written over time to convert graphics to the charset and BGMap formats used on the Virtual Boy. However, they are either poorly documented and hard to use or they have little or no support for BGMaps, forcing programmers to use inefficient ways of storing graphics, like having a separate charset for each BGMap.
This program, however, can take multiple BMP files at once and convert them to BGMaps that all share the same charset, with the charset only containing as many characters as needed to conserve space and the BGMaps only containing as many tiles as needed, not all 64 by 64.
- This topic was modified 11 years, 9 months ago by HorvatM.
Attachments:
I just had a look at this again. This really solves allot issues. Until I started with all of the ROM hacks I didn’t realize how un-efficiently I treat the BGMaps and CharSeq.
Although I haven’t yet had need to try it, from the docs it seems to have a few limitations. While none of these is a “deal-breaker”, they can make it a bit harder to use.
1) Limited to Windows BMP files for input
You might want to check out FreeImage. It’s cross-platform, very easy to use, and supports tons of input formats! It even has handy auxiliary functions; e.g. to reduce colors or convert an image to greyscale.
2) User is required to specify all filenames, without extensions
This makes it harder to use in a makefile.
3) Only C or raw binary output
C takes longer to parse, and binary requires an asm “stub” to link. GNU assembly output shouldn’t be too hard to add, right?
4) Not open-source
😉
RunnerPack wrote:
1) Limited to Windows BMP files for input
I’m trying to keep it simple. If you need other formats, you can use ImageMagick or something to convert them. I’ll add support for images with fewer colors though so you won’t have to waste space with 24-bit BMPs. Although NTFS compression works quite well on them.
2) User is required to specify all filenames, without extensions
This makes it harder to use in a makefile.
What’s your proposed alternative?
3) Only C or raw binary output
C takes longer to parse, and binary requires an asm “stub” to link. GNU assembly output shouldn’t be too hard to add, right?
Right. What does it need? A label and a list of .dbs? I’m not very familiar with the GNU assembler – I’ve only used it with the asm() macro in C code.
4) Not open-source
😉
No comment on that.
HorvatM wrote:
RunnerPack wrote:
2) User is required to specify all filenames, without extensions
This makes it harder to use in a makefile.
What’s your proposed alternative?
Well, being the programmer, I would say that’s really your decision, but here’s what I know:
Make’s job is to make an output file for each input file – whenever the input file is newer or the output file doesn’t exist – using a set of rules. When a rule is executed by make, it will insert the name of the input and output files into the rule’s command-line(s). You can tell make to provide the name without the extension, but it would be nice if it wasn’t necessary.
This is really the only problem, since your goal of making one charset out of a series of images doesn’t really mesh with make’s idea of calling a tool once for each input file, anyway. I’ll write more about this in the gritVB thread…
3) Only C or raw binary output
Right. What does it need? A label and a list of .dbs?
Yes, the assembly file needs a label and the actual data directives, but you also need a header with the C declaration. Note: while gcc 2.95 does use .db, .dw, and .dd, version 4.x uses .byte, .int, and .word, respectively. I guess these guys never heard the one about not fixing what isn’t broken 😛
Here’s a before and after example:
The existing C header:
const u16 maptest[] = { 0x0000, ...
The desired ASM code:
.global maptest maptest: .dw 0x0000, ...
Or, for GCC 4.x:
.global maptest maptest: .int 0x0000, ...
Now, to get the C compiler to see the label, it has to be declared “external” like so:
extern const u16 maptest[];
(Not sure about the const part, actually).
Another thing I noticed when I tried VBIMGC is that your headers don’t have “guards” around them to prevent multiple declarations, and such. You might want to do something like:
#ifndef _MAPTEST_H_ #define _MAPTEST_H_ const u16 maptest[] = { 0x0000, ... } #endif
I used this a while ago and I noticed one thing: If you reference your image including a path (like: vbimgc /XC chr bgm horst C:\vbde\image_0) you will have the whole path inside of the header file (const u16 bgmC:\vbde\image_0[] = {).
I never thought about that. The way I use it is to create a batch file in the same directory as the images and run it each time I want to regenerate the output.
Maybe there should be an option to use a file that contains a list of files to process and how to name the resulting arrays? This way, it would also be possible to specify different settings for each image and different output filenames.
HorvatM wrote:
Maybe there should be an option to use a file that contains a list of files to process and how to name the resulting arrays? This way, it would also be possible to specify different settings for each image and different output filenames.
That is an excellent idea which I fully support. It sounds pretty easy to implement, too; just pass each line of the file through your command-line argument parser.
BTW, what does your program do when the files you give it generate more than 2048 chars?
I played around with this today and noticed something strange:
– I converted a batch of images
– I did not use the optimization features
– I copied the whole charset to memory
– I copied the whole BGMap of image 1 to bgmap 31
– When pressing a button I would copy the second image bgmap 31
– The first image was bigger then the second one
-> The first image would display correctly
-> The second image would display correctly but you would still see the rests of the first image
I double checked and the generated bgmap of the second image actually contains parts of the first one.
I know that it doesn’t really matter as you can set the world attributes to not show the garbage around the image but I though you still might want to know.
It’s because the array in which BGMaps are generated is not initialized before generating each one. Should all cells be initialized to 0? That would only give you a blank BGMap if character 0 was blank though.
HorvatM wrote:
It’s because the array in which BGMaps are generated is not initialized before generating each one. Should all cells be initialized to 0? That would only give you a blank BGMap if character 0 was blank though.
I guess that would work most of the time. Alternatively, you could search for the blank char and use it to initialize the maps.
When I worked on Fishbone I had quite some trouble organizing maps and chars I created. The basic problem is that after all of the tools always assume there will only be one charset (or 4). However, I often reused certain sprites forcing me to have them multiple times on different maps.
Do you think it would be possible, when converting a batch of images, to specify an offset for the chars? The charset itself would stay the same but the maps would reference to the respective chars + offset. That way someone could have multiple charsets and bgmaps.
I don’t need this right now though. It’s just something I remembered when I looked at your tool.
thunderstruck wrote:
When I worked on Fishbone I had quite some trouble organizing maps and chars I created. The basic problem is that after all of the tools always assume there will only be one charset (or 4). However, I often reused certain sprites forcing me to have them multiple times on different maps.Do you think it would be possible, when converting a batch of images, to specify an offset for the chars? The charset itself would stay the same but the maps would reference to the respective chars + offset. That way someone could have multiple charsets and bgmaps.
I did just that in Advanced Pasta Cooking Simulator (I should release the source code for it some day – BTW, you should play with it a bit because there’s a cool “easter egg” in it). The program doesn’t have any option to do this, but you can easily do the same thing with efficient charsets. That’s how I did it (with efficient BGMaps as well, but you don’t have to):
// Pointer to VIP character memory #define vbCharmem ((u16*)0x78000) // Character indexes for LoadEfficientBGMap and LoadEfficientCharset #define vbkCharseg0Index 0 #define vbkCharseg1Index 512 #define vbkCharseg2Index 1024 #define vbkCharseg3Index 1536 void LoadEfficientBGMap ( const u16* Src, unsigned int BGMap, unsigned int CharIndexBase ) { // Loads an efficiently stored VBIMGC-produced BGMap and optionally adjusts // the character indexes of its cells. unsigned int X; unsigned int Y; unsigned int Width; unsigned int Height; unsigned int Index; Width = Src[0] & 0x00FF; Height = Src[0] >> 8; Index = 1; for(Y = 0; Y < Height; Y++) for(X = 0; X < Width; X++) BGMM[BGMap * 0x1000 + Y * 64 + X] = Src[Index++] + CharIndexBase; } unsigned int LoadEfficientCharset (const u16* Src, unsigned int CharIndex) { // Loads an efficiently stored VBIMGC-produced charset and returns the // index of the character immediately following it in the VIP's character // memory. unsigned int i; unsigned int Length; CharIndex *= 8; // Convert to row index Length = Src[0] * 8; for(i = 1; i <= Length; i++) vbCharmem[CharIndex++] = Src[i]; return CharIndex / 8; } void Init () { unsigned int CharIndex; // Font for vbTextOut LoadEfficientCharset(kChr_Font, vbkCharseg3Index + 32); // Sprites LoadEfficientBGMap(kBGM_SpritesLR, kBGMISprites, vbkCharseg1Index); CharIndex = LoadEfficientCharset(kChr_Sprites, vbkCharseg1Index); // Door LoadEfficientBGMap(kBGM_DoorLR, kBGMIDoor, CharIndex); CharIndex = LoadEfficientCharset(kChr_Door, CharIndex); // Stove view LoadEfficientBGMap(kBGM_StoveViewL, kBGMIStoveL, CharIndex); LoadEfficientBGMap(kBGM_StoveViewR, kBGMIStoveR, CharIndex); CharIndex = LoadEfficientCharset(kChr_StoveView, CharIndex); // Counter view LoadEfficientBGMap(kBGM_CounterViewL, kBGMICounterL, CharIndex); LoadEfficientBGMap(kBGM_CounterViewR, kBGMICounterR, CharIndex); CharIndex = LoadEfficientCharset(kChr_CounterView, CharIndex); // Fridge LoadEfficientBGMap(kBGM_FridgeLR, kBGMIFridge, CharIndex); CharIndex = LoadEfficientCharset(kChr_Fridge, CharIndex); // Outside view LoadEfficientBGMap(kBGM_OutsideViewLR, kBGMIOutside, CharIndex); CharIndex = LoadEfficientCharset(kChr_OutsideView, CharIndex); // Clouds LoadEfficientBGMap(kBGM_CloudsLR, kBGMIClouds, CharIndex); CharIndex = LoadEfficientCharset(kChr_Clouds, CharIndex); // (...) }
cool, thanks for that code. Another thing I was wondering: Vide is reducing the number of chars by setting the flip value in the bgmaps. Meaning, if two chars are the same but flipped it will save the char only once and set the values in the bgmaps. Are you doing that as well? From looking at the resulting charset it doesn’t look like it but my testsprite might not be a 100% accurate.
I’m finally releasing version 1.1. This is the version RunnerPack and I used for Deathchase, and some (all?) of the graphics can’t be converted with version 1.
Changes:
– Now also supports 32-bit BMPs.
– You can convert BMPs to charsets “one to one” without generating BGMaps (useful for fonts).
– The number of new unique characters for each BGMap generated is reported, along with a total at the end of the conversion.
– If an exact color match isn’t found, the closest one is used (thanks to RunnerPack).
– The BGMap buffer is now cleared before converting each BMP (reported by thunderstruck).
– The program now checks for the 2048-character limit before adding a new character.
– Updated the LoadEfficientCharset and LoadEfficientBGMap functions in the readme with those from Deathchase.
Attachments:
Cool stuff. I used version 1 for quite some time. I had to program my own in the end because I needed the char flip feature. Any plans on putting that in?
I actually implemented that, but it sometimes produced incorrect results. I decided to release it without this feature anyway because it’s already been more than a year since the first version.
Is a Unix or source release possible for those who do not use Windows? I’ve been batting this around, but at least for my project, it may be beneficial to include the raw assets, and have the build system convert them to tilemaps as a “preprocessing” step before compilation, but I’m not sure.
cr1901 wrote:
Is a Unix or source release possible for those who do not use Windows?
What OS do you use?
I’ve been batting this around, but at least for my project, it may be beneficial to include the raw assets, and have the build system convert them to tilemaps as a “preprocessing” step before compilation, but I’m not sure.
I’m open to suggestions. In particular, if you mean the listing file idea discussed a few posts ago, suggestions would be really welcome, because I’m not entirely sure myself how it should be implemented.
HorvatM wrote:
What OS do you use?
Actually, Windows :P, but chances are by the end of this year, I’ll switch to Linux or BSD full time. Just looking out for those who use a Unix besides OSX. Additionally, since other users use a Cygwin version of gccVB, and my build system (SCons) can run on Cygwin, a cygwin version I’m sure would be useful so that the build system can run to completion (I’m not sure if Windows exes can be called directly from Cygwin).
The more I think about it, the more I think including the raw assets and generating the source files makes sense.
I’m open to suggestions. In particular, if you mean the listing file idea discussed a few posts ago, suggestions would be really welcome, because I’m not entirely sure myself how it should be implemented.
If you refer to the ASM output, I have a tool on github (disasm-utils) which can take your binary output and convert it to an ASM file.
HorvatM wrote:
Maybe there should be an option to use a file that contains a list of files to process and how to name the resulting arrays? This way, it would also be possible to specify different settings for each image and different output filenames.
This. This would be a very useful feature for me. I’m running into the same issues as thunderstruck, since my build system never leaves the top level directory. My “real” source files expect a specific format for the char and bg array names, and path qualification with screw the expected names up.