Last active
September 23, 2018 18:58
-
-
Save city41/c7645fdf79fc7e7ae91c44e50ddc3087 to your computer and use it in GitHub Desktop.
Ardynia's loadRoom (RLE decompression) method
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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