#Reading images/bmp(?) from binary file

80 messages · Page 1 of 1 (latest)

sonic iglooBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

vital moth
#

looks to me like all the logic regarding the actual file format would be in the functions this would call

#

dunno, i don't see much that would help up there

#

you're probably gonna have to reverse engineer the format

#

unless there's more code

#

mandatory disclaimer ofc: since you almost certainly don't hold the copyright to the art in these files, you generally can't redistribute whatever you may be able to scrape out of them. and ofc depending on where you are located, and the original license of the software, reverse engineering might be illegal.

#

well, this is just using the stuff it got from somewhere

#

the interesting part would be where stuff is actually read from the file

#

is this code on github somewhere?

#

k

#

is the data all in one huge file?

#

or are there many individual files?

#

ok

#

well, the standard trick is to try to open the file with a zip program

#

ok

#

that's ofc unfortunate

#

i don't see any useful clues in the code above

#

lol

#

ah

#

well that contains some clues

stiff fulcrum
#

Oh, you're trying to read data from a binary file.

#

My bad.

vital moth
#

some library

#

but it says that it's using lzma compression

stiff fulcrum
#

A compression algorithm.

vital moth
#

a compression algorithm

#
void compressBitmap(Bitmap& bitmap)
{
  if (bitmap.compressionMethod!=0)return;
  bitmap.compressionMethod=3;
  Fay::MemoryStream uncompressed;
  uncompressed.write(bitmap.data.data,bitmap.data.size); //fixme: use a wrapper stream instead
  uncompressed.seek(0);
  Fay::MemoryStream compressed;
  auto ret=Fay::lzmaCompress(uncompressed,compressed);
  assert(ret);
  delete[] bitmap.data.data;
  bitmap.data.data=new byte[compressed.size()];
  bitmap.data.size=compressed.size();
  memcpy(bitmap.data.data,compressed.data(),compressed.size());
  bitmap.data.crc=crc32(0,bitmap.data.data,bitmap.data.size);
}
```this pretty much describes how it writes bitmaps to a stream of bytes
#

apparently

#

sure: a lot of hard and very technical work

#

from what i've seen, it seems that this game stores a bunch of sprite sheets as compressed bitmaps in a sort of archive format

#

the function above is probably pretty descriptive of what each sprite sheet looks like on disk

#

at least if i'm understanding this correctly

#

knowing the things we know now, it should be very possible to reverse engineer the format

#

probably relatively easy actually

#

at least for someone with the necessary expertise and motivation to do so ^^

#

np ^^

#

i'm guessing GFXC is the format you're trying to figure out

#
  TBitStream header;
  header.writeRawString("GFXC");
  header.writeBits(3,8); //version
  header.writeBits(bitmaps.size(),16);
  header.writeBits(0,24); //header size
  foreach(bitmap,bitmaps)bitmap->serialize(header);
  const int headerSize=header.getSize();
  header.seek(7);
  header.writeBits(headerSize,24);
  uint currentPos=headerSize;
  foreach(bitmap,bitmaps)
  {
    bitmap->dataPosition=currentPos;
    bitmap->serialize(header);
    currentPos+=bitmap->data.size;
  }
  header.flush();
 
  TBitStream full(getBasePath()+"/data",BSFM_FILE|BSFM_READWRITE|BSFM_TRUNCATE);
  header.seek(0);
  full.write(header,header.getSize());
  foreach(bitmap,bitmaps)full.write(bitmap->data.data,bitmap->data.size);
```this would seem to be pretty much what it looks like
#

at least it provides the general structure

#

even if it doesn't tell us what's inside all the header and stuff

#

i guess it's

  • GFXC followed by
  • one byte that contains the version number,
  • 2 bytes that contain the number of bitmaps in the file,
  • 3 bytes that contain the size of the header,
  • then the header contents, which are just
    • the headers of each bitmap in the file
  • then the actual contents of each bitmap, likely compressed
#

this is definitely gonna be quite some work

#

but yeah

#

seems doable

#

is this the actual source of the game?

#

because this looks like the source of some tool that produces those files

#

ok ^^

#

yeah this is basically the other part

#

that tells you what the bitmap headers look like i guess

#
  void serialize(TBitStream& stream) const
  {
    stream.writeBits(width,16);
    stream.writeBits(height,16);
    stream.writeBits(bitmapWidth,16);
    stream.writeBits(bitmapHeight,16);
    stream.writeBits(leftBorder,16);
    stream.writeBits(upperBorder,16);
    stream.writeBits(getRValue(transparentColor),8);
    stream.writeBits(getGValue(transparentColor),8);
    stream.writeBits(getBValue(transparentColor),8);
    stream.writeBits(bytesPerPixel*8,8);
    stream.writeBits(group,32);
    stream.writeBits(id,32);
    stream.writeString(name,8);
    stream.writeBits(compressionMethod,8);
    stream.writeBits(compressionParams,8);
    //stream.writeBits(0,32); //?
    stream.writeBits(dataPosition,32);
    stream.writeBits(data.size,32);
  }
#

yeah i mean, with this, you have basically the entire format mapped out

#

at least so it would seem to me

#
  • GFXC followed by
  • 1 byte that contains the version number,
  • 2 bytes that contain the number of bitmaps in the file,
  • 3 bytes that contain the size of the header,
  • then the headers of each bitmap in the file
    • 2 bytes width
    • 2 bytes height
    • 2 bytes bitmapWidth (?maybe tile width?)
    • 2 bytes bitmapHeight (?maybe tile height?)
    • 2 bytes leftBorder (?)
    • 2 bytes upperBorder (?)
    • 3 bytes transparent color RGB
    • 1 byte number of bits per pixel
    • 4 bytes group number (?)
    • 4 bytes id
    • a string name (I'm guessing 1 byte for the length followed by length bytes for the string content)
    • 1 byte compression method
    • 1 byte compression parameters
    • 4 bytes offset in the file to the start of the bitmap data
    • 4 bytes size of the bitmap data
  • then the actual contents of each bitmap, likely compressed
#

updated

#

a C++ thing ^^

#

i think as far as getting the data ouf of those files is concerned, that's pretty much everything

brazen verge
#

.hpp is a cpp header file extension (usually)
It's mainly used in projects with bot C and C++ where they want to make sure nobody accidentally uses a C++ header in C (or by people having been burned by that before enough to type those 2 letters per include)

vital moth
#

shouldn't be too hard to roll a python script with ctypes or smth that just reads those files

#

the decompression is gonna be the only really tricky bit

#

i don't see any encryption there

#

just compression

brazen verge
#

Depends on the data, but for assets, usually not, especially if it hasn't been online for a few years

vital moth
#

it seems that compression method can either be 0, in which case the data is uncompressed, or 3, in which case it's compressed with BCM_LZMA, whatever that is exactly (some variation of the LZMA algorithm i suppose)

#

do note that there's a lot of guesswork there based on just a cursory glance of those fragments of code ^^

#

i'm not sure what more to look for

#

we have pretty much figured out the format

#

at least assuming that code is indeed the correct code ^^

#

with the info above, it should be possible to extract the bitmaps

#

well, you said the code is not from the actual game

#

so ofc there could have been changes

#

and so the actual files might be different

#

based on this screenshot, there would supposedly be about 20000 bitmaps in that file

#

no idea if that makes sense

#

seems a bit much

sonic iglooBOT
#

@livid olive Has your question been resolved? If so, type !solved :)

vital moth
#

i don't see any file format things in there

#

at least upon quick glance

#

this seems to just generate some font stuff?

sonic iglooBOT
#

Unexpected parameters provided

Usage:
!solved

#

Thank you and let us know if you have any more questions!

This thread is now set to auto-hide after an hour of inactivity