Sorry but this is going to be kinda long...
So I'm making an application that takes a bitmap and compresses it into a .map file for one of my games. The map data basically consists only of walkable and non walkable area (which is represented by black and white in the bitmap).
So I load in a black and white bitmap (which is still 24bpp by the way) and check if tiles of a certain area are white or black, then I bitwise 8 bool values into one byte. I keep going untill the entire thing is encoded.
I'm starting with really tiny maps: 128 x 104 pixels.(the game is for cell phones) and each tile is 8x8 (16 tiles across and 13 down). I start with a bitmap thats 39kb and the result is a .map file that is 28 bytes.
All of this worked fine untill I decompressed the data on my cell phone emulator and got unexpected results. I searched for the problem for ages but I didn't find it until I opened my .map files in a hex editor (admittedly I'm not great with hex but I figured It might help somehow).
For some reason I find that the name of the file is encoded in the map data. For instance for TestMap3.map (made from TestMap3.bmp) this is the hex:
00000000:10 0d 00 00 0c 00 00 00 ff ff ff ff 54 65 73 74 -> ........ÿÿÿÿTest
00000010:4d 61 70 33 2e 62 6d 70 00 00 00 00 ->Map3.bmp....
Since the file is only just big enough to store the map data, of course it must be obstructed by the name. I can't figure out for the life of me how the name got in my data. All my compression algorithm does is pack 8 bools into a byte depending on whether the tiles are black or white...
By the way the file above is made from a bitmap that's totally black, so I expected something to the effect of: 00 00 00 00 00 00... ect.
So heres my code. Its rushed and its really nothing fancy...
All the cout<< statements are to check values to find errors.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int* GetBMPData(string,unsigned char**);
int ShrinkData(unsigned char*, int, int, unsigned char**);
void createMap(string, unsigned char* shrunkData, int);
const string FILE_NAME="TestMap3";
const int TILE_DEMENSIONS = 8;
const int WIDTH=0;
const int HEIGHT=1;
int numbytes;
int main()
{
unsigned char* data;
unsigned char* compressedData;
int* demensions = GetBMPData(FILE_NAME.data(), &data);
int shrankSize=ShrinkData(data, demensions[WIDTH], demensions[HEIGHT], &compressedData);
createMap(FILE_NAME, compressedData, shrankSize);
getchar();
}
int* GetBMPData(string bmpFileName, unsigned char** data)
{
unsigned char offset[4]; //stores the offset to the RGB data
unsigned char width[4]; //stores the width of the image
unsigned char height[4]; //stores the height of the image
bmpFileName+=".bmp";
ifstream bitstream(bmpFileName.data()); //opens the file passed into the function
bitstream.seekg(10); //seeks to the offset vale
bitstream.get((char*)offset, 4);
bitstream.seekg(18); //seek to the width
bitstream.get((char*)width,4);
bitstream.seekg(22); //seek to the height
bitstream.get((char*)height,4);
numbytes= (*((int*)width)) * (*((int*)height)) * 3; //numbytes equals the width times the height times three (the thre RGB compotents of a pixel) gotta love c++ right?
if((*data)!=NULL)
delete (*data);//(this is supposed to prevent memory leaks)
*data=(unsigned char*) new char[numbytes]; //data equals a new array which is just the right size
bitstream.seekg(*offset); //seek to the RGB data
bitstream.get((char*)(*data), numbytes);
bitstream.close(); //close the stream... best to be tidy right?
cout<<"Offset: "<<*((int*)offset)<<"\nWidth: "<<*((int*)width)<<"\nHeight: " <<*((int*)height)<<"\n"; //check the values just in case
int* returnVal = new int[2]; // return array for the width and height
returnVal[WIDTH]=*((int*)width); //put width into first slot
returnVal[HEIGHT]=*((int*)height); //and height into second
return returnVal;
}
int ShrinkData(unsigned char* data, int width, int height, unsigned char** shrunk)
{
int numOfBlack=0;
int numOfWhite=0;
int numMapXTiles = width/TILE_DEMENSIONS;
cout<<width/TILE_DEMENSIONS<<"\n";
cout<<height/TILE_DEMENSIONS<<"\n";
int numMapYTiles = height/TILE_DEMENSIONS;
int eightcount=0;
int shrunkSize=((numMapXTiles*numMapYTiles)/8) + 2;
*shrunk = new unsigned char[shrunkSize];
int offset=2; //offsets the firs two initial values
(*shrunk)[0]=numMapXTiles;
(*shrunk)[1]=numMapYTiles;
for(int i=0; i<numMapYTiles; i++)
{
for(int j=0; j<numMapXTiles; j++)
{
int bitPlace= eightcount%8; //this variable is how many places I shift the bits for compression
int dataOffset=(i*numMapXTiles*TILE_DEMENSIONS*TILE_DEMENSIONS*3) + (j*TILE_DEMENSIONS*3);
unsigned char* shrunkByte=((*shrunk)+(eightcount/8)+ offset);
unsigned char bitWiser;
unsigned char tileColor=*(data+dataOffset);
if( tileColor == 255)
{
bitWiser=1;
numOfWhite++;
}
else if( tileColor == 0)
{
bitWiser=0;
numOfBlack++;
}
else
{
cout<<"Fatal Error: "<<"\n";
}
//cout<<(i*16)+j<<": "<<(int)tileColor<<"\n";
//cout<<(i*16)+j<<": "<<(i*numMapXTiles*TILE_DEMENSIONS*TILE_DEMENSIONS*3) +(j*TILE_DEMENSIONS*3)<<"\n";
bitWiser<<bitPlace;
*shrunkByte=(*shrunkByte) | bitWiser;
eightcount++;
}
}
cout<<"Number of white tiles: "<<numOfWhite<<"\nNumber of black tiles: "<<numOfBlack;
//Note the white and black values are correct here but not after I decompress
return shrunkSize;
}
void createMap(string fileName, unsigned char* shrankData, int shrankSize)
{
fileName+=".map";
ofstream mapFile(fileName.data(), ios::binary);
mapFile.write((const char*)shrankData,shrankSize);
mapFile.close();
}
Sorry if thats hard to understand. Like I said its rushed. It was my goal to make the entire map system in one day. I thought it'd be simple enough.
I did loads of debugging and cout-ing and I'm certain that all the data is correct when its loaded. The numOfBlack and numOfWhite variables are always right so I figure the compression is working well. For some reason though the .map file only contains the bitmap file name and a bit of useless data.
In order for the code to work a white and black bitmap (24bpp) must be in the same path. Best to make it 128x104 if you run it yourself. I'm not sure how flexible it is.
I should also tell you that when I used more white tiles (int the example near the top there were 0 white tiles) the name of file begins to get messed up in the hex file. For instance TestMap1 used half white tiles and half black and here's the hex:
00000000:10 0d 00 00 0d 01 00 00 ff ff ff ff 55 65 73 74 ........ÿÿÿÿUest
00000010:4d 61 70 31 2f 63 6d 70 01 01 00 00 Map1/cmp....
if anyone could help me with this I would greatly appreciate it. I'm really eager to get on with development. I'll elaborate on any part of the code that doesn't make sense to anyone.
[Edited by - ForeverNoobie on December 26, 2008 6:38:40 PM]
Simplicity is the ultimate sophistication. – Leonardo da Vinci