Skip to content

Instantly share code, notes, and snippets.

@city41
Last active September 23, 2018 18:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save city41/c7645fdf79fc7e7ae91c44e50ddc3087 to your computer and use it in GitHub Desktop.
Save city41/c7645fdf79fc7e7ae91c44e50ddc3087 to your computer and use it in GitHub Desktop.
Ardynia's loadRoom (RLE decompression) method
/**
* Load a compressed room from progmem into RAM
*
* maps are compressed in two ways:
*
* * two tiles per byte, as there are less than 16 tiles
* * run length encoding is employed to remove long runs of the same tile
*
* The RLE is done per room, and there is a header section containing the starting
* index of each room. This is because with RLE compression, each room will have a different
* number of bytes
*
* The general decompression approach:
* 1) convert the roomX and roomY coordinate into a linear coordinate
* 2) with linear coordinate in hand, dive into the room indices part of the header
* and grab the starting index of the room. This starting index does NOT account for the header!
* 3) grab the starting index of the next room, that way we can figure out how many bytes
* are for the current room
* 4) iterate over the room's bytes, one nibble at a time.
* -- if the nibble is just a regular tile, then dump it
* -- if the nibble is the compression indicator (0xF, ie 16), then read the next nibble
* to get the count (how many times the tile will be repeated). Then read the next nibble
* to learn what the tile to render is
* dump that tile count times
*
*
* the offset param: there is room for two rooms in RAM, and they live in the same array, so offset is either zero
* (headed into the front of the array), or is 28 (headed into back of the array). Two rooms need to be decompressed
* at the same time because need to render two rooms during a room transition
*/
void TileRoom::loadRoom(uint8_t roomX, uint8_t roomY, uint8_t offset) {
uint16_t lengthOfMap = pgm_read_word(map);
uint8_t mapWidth = pgm_read_byte(map + 2);
uint8_t mapHeight = pgm_read_byte(map + 3);
uint8_t numRooms = mapWidth * mapHeight;
// how far into the map is this room?
uint8_t roomNumber = getRoomIndex(roomX, roomY);
// grab its starting data index out of the room indices header
// the stored index does not account for the headers, so tack them on
// numRooms * 2 -> get past the room indice words
// + MAP_HEADER_SIZE -> get past the map width, map height and tile size
uint16_t roomIndex = pgm_read_word(map + MAP_HEADER_SIZE + roomNumber * 2) + (numRooms * 2) + MAP_HEADER_SIZE;
uint16_t nextRoomIndex;
if (roomNumber < numRooms - 1) {
nextRoomIndex = pgm_read_word(map + MAP_HEADER_SIZE + (roomNumber * 2) + 2) + (numRooms * 2) + MAP_HEADER_SIZE;
} else {
nextRoomIndex = lengthOfMap;
}
uint8_t numNibbles = (nextRoomIndex - roomIndex) * 2;
uint8_t curNibbleIndex = 0;
uint8_t maxOffset = offset + TILES_PER_ROOM;
while (curNibbleIndex < numNibbles) {
uint8_t rawTileByte = pgm_read_byte(map + roomIndex + (curNibbleIndex >> 1));
uint8_t nibble = (curNibbleIndex & 1) ? rawTileByte & 0xF : rawTileByte >> 4;
if (nibble == Compression) {
uint8_t nextRawTileByte = pgm_read_byte(map + roomIndex + ((curNibbleIndex + 1) >> 1));
// a count nibble can be either at the top or bottom of a byte, basically compression treats
// nibbles as first class
uint8_t count = ((curNibbleIndex + 1) & 1) ? nextRawTileByte & 0xF : nextRawTileByte >> 4;
uint8_t nextNextRawTileByte = pgm_read_byte(map + roomIndex + ((curNibbleIndex + 2) >> 1));
uint8_t tileId = ((curNibbleIndex + 2) & 1) ? nextNextRawTileByte & 0xF : nextNextRawTileByte >> 4;
// memset here?
for (uint8_t c = 0; c < count; ++c) {
rooms[offset++] = tileId;
}
// jump past the compression block of <compression nibble><count nibble><template nibble>
curNibbleIndex += 3;
} else {
// need to make sure don't go beyond the room offset in the case of when a compression
// run leaves a dead nibble at the end of the room, otherwise will clobber other memory
if (offset < maxOffset) {
rooms[offset++] = nibble;
}
curNibbleIndex += 1;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment