diff --git a/.vscode/launch.json b/.vscode/launch.json index 555855f..bf47678 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -46,7 +46,7 @@ { "description": "Load additional app binary", "ignoreFailures": true, - "text": "add-symbol-file ${workspaceRoot}/isofiles/apps/init.bin 0x800000" + "text": "add-symbol-file ${workspaceRoot}/isofiles/apps/compositor.bin 0x800000" } ] }, @@ -79,7 +79,7 @@ { "description": "Load additional app binary", "ignoreFailures": true, - "text": "add-symbol-file ${workspaceRoot}/isofiles/apps/init.bin 0x800000" + "text": "add-symbol-file ${workspaceRoot}/isofiles/apps/compositor.bin 0x800000" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 20fe05d..0bd6739 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -47,7 +47,11 @@ "memory_resource": "cpp", "hash_map": "cpp", "list": "cpp", - "iterator": "cpp" + "iterator": "cpp", + "bit": "cpp", + "ctime": "cpp", + "numeric": "cpp", + "cinttypes": "cpp" }, "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: WebKit, PointerAlignment: Left}", "python.pythonPath": "/usr/bin/python3" diff --git a/apps/desktop/item.h b/apps/desktop/item.h index 0d5da8d..afc6740 100644 --- a/apps/desktop/item.h +++ b/apps/desktop/item.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include using namespace LIBCactusOS; using namespace LIBCactusOS::Imaging; @@ -35,14 +35,11 @@ void DesktopItem::DrawToContext() { if(this->iconBuffer) { - Image img = ConvertBMPRaw(this->iconBuffer); + Image img = PNGDecoder::ConvertRAW(this->iconBuffer); for(int x = 0; x < img.GetWidth(); x++) for(int y = 0; y < img.GetHeight(); y++) { uint32_t argb = img[y * img.GetWidth() + x]; - if(argb != 0xFFFFFFFF) - this->context->canvas->SetPixel(x, y, argb); - else - this->context->canvas->SetPixel(x, y, 0x00000000); + this->context->canvas->SetPixel(x, y, argb); } } if(this->drawLabel) { diff --git a/isofiles/desktop/browser.bmp b/isofiles/desktop/browser.bmp deleted file mode 100644 index 141cf16..0000000 Binary files a/isofiles/desktop/browser.bmp and /dev/null differ diff --git a/isofiles/desktop/calc.bmp b/isofiles/desktop/calc.bmp deleted file mode 100644 index 88456e8..0000000 Binary files a/isofiles/desktop/calc.bmp and /dev/null differ diff --git a/isofiles/desktop/calculator.png b/isofiles/desktop/calculator.png new file mode 100644 index 0000000..e5391c9 Binary files /dev/null and b/isofiles/desktop/calculator.png differ diff --git a/isofiles/desktop/explorer.png b/isofiles/desktop/explorer.png new file mode 100644 index 0000000..42e953a Binary files /dev/null and b/isofiles/desktop/explorer.png differ diff --git a/isofiles/desktop/items.txt b/isofiles/desktop/items.txt index 9435703..640fecf 100644 --- a/isofiles/desktop/items.txt +++ b/isofiles/desktop/items.txt @@ -3,8 +3,8 @@ This file contains the items that are present on the desktop This file is parsed by the desktop application on startup ////////////////// -"Calculator" file="B:\apps\calc.bin" icon="B:\desktop\calc.bmp" -"Terminal" file="B:\apps\terminal.bin" icon="B:\desktop\term.bmp" -"Power Manager" file="B:\apps\powermanager.bin" icon="B:\desktop\power.bmp" -"Mines" file="B:\apps\mines.bin" icon="B:\desktop\mine.bmp" -"Browser" file="B:\apps\browser.bin" icon="B:\desktop\browser.bmp" +"Calculator" file="B:\apps\calc.bin" icon="B:\desktop\calculator.png" +"Terminal" file="B:\apps\terminal.bin" icon="B:\desktop\terminal.png" +"Power Manager" file="B:\apps\powermanager.bin" icon="B:\desktop\power.png" +"Mines" file="B:\apps\mines.bin" icon="B:\desktop\mine.png" +"Browser" file="B:\apps\browser.bin" icon="B:\desktop\explorer.png" diff --git a/isofiles/desktop/mine.bmp b/isofiles/desktop/mine.bmp deleted file mode 100644 index e4b5021..0000000 Binary files a/isofiles/desktop/mine.bmp and /dev/null differ diff --git a/isofiles/desktop/mine.png b/isofiles/desktop/mine.png new file mode 100644 index 0000000..00961b8 Binary files /dev/null and b/isofiles/desktop/mine.png differ diff --git a/isofiles/desktop/power.bmp b/isofiles/desktop/power.bmp deleted file mode 100644 index 3fac31d..0000000 Binary files a/isofiles/desktop/power.bmp and /dev/null differ diff --git a/isofiles/desktop/power.png b/isofiles/desktop/power.png new file mode 100644 index 0000000..b4498db Binary files /dev/null and b/isofiles/desktop/power.png differ diff --git a/isofiles/desktop/term.bmp b/isofiles/desktop/term.bmp deleted file mode 100644 index 51ca13d..0000000 Binary files a/isofiles/desktop/term.bmp and /dev/null differ diff --git a/isofiles/desktop/terminal.png b/isofiles/desktop/terminal.png new file mode 100644 index 0000000..f7d4620 Binary files /dev/null and b/isofiles/desktop/terminal.png differ diff --git a/lib/include/bitreader.h b/lib/include/bitreader.h new file mode 100644 index 0000000..5ee100d --- /dev/null +++ b/lib/include/bitreader.h @@ -0,0 +1,72 @@ +#ifndef __LIBCACTUSOS__BITREADER_H +#define __LIBCACTUSOS__BITREADER_H + +#include + +namespace LIBCactusOS +{ + // Used to read specific bits of a datastream + class BitReader + { + private: + uint8_t* dataPtr = 0; + uint32_t pos = 0; + uint8_t byte = 0; + uint32_t numBits = 0; + public: + BitReader(uint8_t* data) + { + this->dataPtr = data; + this->pos = 0; + this->byte = 0; + this->numBits = 0; + } + + // Read single byte + uint8_t ReadByte() + { + // Discard other bits + this->numBits = 0; + uint8_t b = this->dataPtr[this->pos]; + this->pos += 1; + return b; + } + + // Read single bit + uint8_t ReadBit() + { + if(this->numBits <= 0) { + this->byte = this->ReadByte(); + this->numBits = 8; + } + this->numBits -= 1; + uint8_t bit = this->byte & 1; + this->byte >>= 1; + return bit; + } + + // Read bits as type + template + T ReadBits(uint32_t n) + { + T ret = 0; + for(uint32_t i = 0; i < n; i++) + ret |= (this->ReadBit() << i); + + return ret; + } + + // Read bytes as type + template + T ReadBytes(uint32_t n) + { + T ret = 0; + for(uint32_t i = 0; i < n; i++) + ret |= (this->ReadByte() << (i*8)); + + return ret; + } + }; +} + +#endif \ No newline at end of file diff --git a/lib/include/gui/events.h b/lib/include/gui/events.h index 4200580..ecc3aef 100644 --- a/lib/include/gui/events.h +++ b/lib/include/gui/events.h @@ -12,7 +12,7 @@ namespace LIBCactusOS class IEventCallback { public: - virtual void Invoke(void*, ArgumentType) = 0; + virtual void Invoke(void*, ArgumentType) {} }; // A callback that is a class method diff --git a/lib/include/heap.h b/lib/include/heap.h index 25342f0..003c426 100644 --- a/lib/include/heap.h +++ b/lib/include/heap.h @@ -13,7 +13,7 @@ namespace LIBCactusOS uint32_t pageRoundUp(uint32_t address); uint32_t pageRoundDown(uint32_t address); - #define HEAP_INCREASE_SIZE 1_MB + #define HEAP_INCREASE_SIZE 10_MB struct MemoryHeader { diff --git a/lib/include/imaging/pngformat.h b/lib/include/imaging/pngformat.h new file mode 100644 index 0000000..8c4981a --- /dev/null +++ b/lib/include/imaging/pngformat.h @@ -0,0 +1,163 @@ +#ifndef __CACTUSOSLIB__IMAGING__PNGIMAGE_H +#define __CACTUSOSLIB__IMAGING__PNGIMAGE_H + +#include +#include +#include +#include + +namespace LIBCactusOS +{ + namespace Imaging + { + #define ZLIB_BUFFER_SIZE 10_MB + + struct PNGChunk + { + uint32_t length; + uint8_t type[4]; + //uint8_t data; + //uint32_t crc; + } __attribute__((packed)); + + struct IHDRChunk + { + uint32_t width; + uint32_t height; + uint8_t bits; + uint8_t colorType; + uint8_t compression; + uint8_t filter; + uint8_t interlace; + } __attribute__((packed)); + + class PNGBuffer + { + public: + uint32_t index = 0; + public: + uint8_t* buffer = 0; + PNGBuffer(uint32_t size) + { + buffer = new uint8_t[size]; + memset(buffer, 0, size); + } + ~PNGBuffer() { } + + void Add(uint8_t byte) + { + buffer[index++] = byte; + } + uint8_t Get(uint32_t i) + { + return buffer[i]; + } + uint32_t Size() + { + return index; + } + }; + + class PNGDecoder + { + private: + static uint8_t PaethPredictor(uint8_t* recon, uint8_t a, uint8_t b, uint8_t c); + static uint8_t Recon_a(uint8_t* recon, uint32_t stride, uint32_t r, uint32_t c); + static uint8_t Recon_b(uint8_t* recon, uint32_t stride, uint32_t r, uint32_t c); + static uint8_t Recon_c(uint8_t* recon, uint32_t stride, uint32_t r, uint32_t c); + public: + // Convert image file into image buffer + static Image Convert(const char* filepath); + + // Create image from array of bytes in png format + static Image ConvertRAW(const uint8_t* rawData); + }; + + // Represents a single node in the huffman tree + class HuffmanNode + { + public: + uint32_t symbol = 0; + HuffmanNode* left = 0; + HuffmanNode* right = 0; + + HuffmanNode() + { + this->symbol = 0; + this->left = 0; + this->right = 0; + } + }; + class HuffmanTree + { + public: + HuffmanNode* root = 0; + + HuffmanTree() + { + this->root = new HuffmanNode(); + } + void Insert(uint32_t codeWord, uint32_t n, uint32_t symbol) + { + HuffmanNode* node = this->root; + HuffmanNode* nextNode = 0; + for(int i = n - 1; i >= 0; i--) { + uint32_t b = codeWord & (1 << i); + if(b) { + nextNode = node->right; + if(nextNode == 0) { + node->right = new HuffmanNode(); + nextNode = node->right; + } + } + else { + nextNode = node->left; + if(nextNode == 0) { + node->left = new HuffmanNode(); + nextNode = node->left; + } + } + node = nextNode; + } + node->symbol = symbol; + } + }; + + struct DecodeTreesResult + { + HuffmanTree* literalLengthTree; + HuffmanTree* distanceTree; + }; + + // Class used to decompress ZLIB data such as png and zip files + class ZLIBDecompressor + { + private: + // Reads data from a non compression block + static void InflateBlockNoCompression(BitReader* reader, PNGBuffer* target); + + // Reads data from dynamic block + static void InflateBlockDynamic(BitReader* reader, PNGBuffer* target); + + // Reads data from static block + static void InflateBlockStatic(BitReader* reader, PNGBuffer* target); + + // Decodes one symbol from bitstream using a HuffmanTree + static uint32_t DecodeSymbol(BitReader* reader, HuffmanTree* tree); + + static void InflateBlockData(BitReader* reader, HuffmanTree* literalLengthTree, HuffmanTree* distanceTree, PNGBuffer* target); + + static HuffmanTree* BitListToTree(List* bitList, List* alphabet); + + static DecodeTreesResult DecodeTrees(BitReader* reader); + public: + // Perform decompression on input and return the complete set of data + static uint8_t* Decompress(uint8_t* input, uint32_t* lenOut = 0); + + // Perform the actual inflation of the DEFLATE block + static uint8_t* Inflate(BitReader* reader, uint32_t* lenOut = 0); + }; + } +} + +#endif \ No newline at end of file diff --git a/lib/include/vector.h b/lib/include/vector.h new file mode 100644 index 0000000..7ac143a --- /dev/null +++ b/lib/include/vector.h @@ -0,0 +1,31 @@ +#ifndef __LIBCACTUSOS__VECTOR_H +#define __LIBCACTUSOS__VECTOR_H + +#include + +namespace LIBCactusOS +{ + template + class Vector + { + private: + uint32_t size = 0; + uint32_t capacity = 0; + T* buffer = 0; + + void reserve(int capacity); + public: + Vector(); + + int Size(); + void push_back(const T& item); + void pop_back(); + void clear(); + + T& GetAt(int n); + T& operator[](int i); + T* data(); + }; +} + +#endif \ No newline at end of file diff --git a/lib/src/heap.cpp b/lib/src/heap.cpp index 8bfb66e..66b0e8a 100644 --- a/lib/src/heap.cpp +++ b/lib/src/heap.cpp @@ -37,7 +37,7 @@ void* UserHeap::Malloc(uint32_t size) Log(Warning, "Heap needs to be expanded for this process"); //Call Kernel - if(DoSyscall(SYSCALL_SET_HEAP_SIZE, endAddress + size + HEAP_INCREASE_SIZE) == SYSCALL_RET_SUCCES) { + if(DoSyscall(SYSCALL_SET_HEAP_SIZE, pageRoundUp(endAddress + size + HEAP_INCREASE_SIZE)) == SYSCALL_RET_SUCCES) { MemoryHeader* lastHeader = 0; //Find last block diff --git a/lib/src/imaging/image.cpp b/lib/src/imaging/image.cpp index 1786510..f361e17 100644 --- a/lib/src/imaging/image.cpp +++ b/lib/src/imaging/image.cpp @@ -7,6 +7,7 @@ #include #include +#include using namespace LIBCactusOS; using namespace LIBCactusOS::Imaging; @@ -126,6 +127,9 @@ Image Image::CreateFromFile(const char* filepath, const char* ext) } } } + else if(strcmp(extension, "png") == 1) { + return PNGDecoder::Convert(filepath); + } else Print("Could not found a image converter for extension %s\n", extension); diff --git a/lib/src/imaging/pngformat.cpp b/lib/src/imaging/pngformat.cpp new file mode 100644 index 0000000..4b8e4d0 --- /dev/null +++ b/lib/src/imaging/pngformat.cpp @@ -0,0 +1,587 @@ +#include +#include +#include +#include +#include +#include +#include + +//#include + +using namespace LIBCactusOS; +using namespace LIBCactusOS::Imaging; + +#define BYTES_PER_PIXEL 4 + +static const uint32_t lengthExtraBits[] = { + 0, 0, 0, 0, 0, 0, 0, 0, //257 - 264 + 1, 1, 1, 1, //265 - 268 + 2, 2, 2, 2, //269 - 273 + 3, 3, 3, 3, //274 - 276 + 4, 4, 4, 4, //278 - 280 + 5, 5, 5, 5, //281 - 284 + 0 //285 +}; + +static const uint32_t lengthBase[] = { + 3, 4, 5, 6, 7, 8, 9, 10, //257 - 264 + 11, 13, 15, 17, //265 - 268 + 19, 23, 27, 31, //269 - 273 + 35, 43, 51, 59, //274 - 276 + 67, 83, 99, 115, //278 - 280 + 131, 163, 195, 227, //281 - 284 + 258 //285 +}; + +static const uint32_t distanceBase[] = { + 1, 2, 3, 4, //0-3 + 5, 7, //4-5 + 9, 13, //6-7 + 17, 25, //8-9 + 33, 49, //10-11 + 65, 97, //12-13 + 129, 193, //14-15 + 257, 385, //16-17 + 513, 769, //18-19 + 1025, 1537, //20-21 + 2049, 3073, //22-23 + 4097, 6145, //24-25 + 8193, 12289, //26-27 + 16385, 24577, //28-29 + 0 , 0 //30-31, error, shouldn't occur +}; + +static const uint32_t distanceExtraBits[] = { + 0, 0, 0, 0, //0-3 + 1, 1, //4-5 + 2, 2, //6-7 + 3, 3, //8-9 + 4, 4, //10-11 + 5, 5, //12-13 + 6, 6, //14-15 + 7, 7, //16-17 + 8, 8, //18-19 + 9, 9, //20-21 + 10, 10, //22-23 + 11, 11, //24-25 + 12, 12, //26-27 + 13, 13, //28-29 + 0 , 0 //30-31 error, they shouldn't occur +}; + +static const uint32_t codeLengthCodesOrder[] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + +void printBT(const char* prefix, const HuffmanNode* node, bool isLeft) +{ + if( node != nullptr ) + { + Print("%s%s%d\n", prefix, (isLeft ? "├──" : "└──" ), node->symbol); + + // enter the next tree level - left and right branch + printBT(str_Combine((char*)prefix, isLeft ? (char*)"│ " : (char*)" "), node->left, true); + printBT(str_Combine((char*)prefix, isLeft ? (char*)"│ " : (char*)" "), node->right, false); + } +} + +void printBT(const HuffmanNode* node) +{ + printBT("", node, false); +} + +Image PNGDecoder::Convert(const char* filepath) +{ + Print("[PNG] Converting image file %s\n", filepath); + + if(FileExists((char*)filepath)) + { + uint32_t fileSize = GetFileSize((char*)filepath); + if(fileSize != (uint32_t)-1) + { + uint8_t* fileBuf = new uint8_t[fileSize]; + ReadFile((char*)filepath, fileBuf); + + Image result = ConvertRAW(fileBuf); + delete fileBuf; + return result; + } + } + + Print("[PNG] Error processing file %s\n", filepath); + return Image(0, 0); +} + +Image PNGDecoder::ConvertRAW(const uint8_t* rawData) +{ + const uint8_t signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + + Image errorImage(0, 0); + IHDRChunk* ihdr = 0; + List imgDataPtrs; + List imgDataLens; + uint8_t* dataPtr = (uint8_t*)rawData; + + if(memcmp(dataPtr, signature, 8) != 0) + return errorImage; + + // Move past the signature + dataPtr += 8; + + // Start reading chunks + while(1) { + PNGChunk* chunk = (PNGChunk*)dataPtr; + uint32_t dataLength = __builtin_bswap32(chunk->length); + + //Print("[PNG] Chunk type = %c%c%c%c Len = %d\n", chunk->type[0], chunk->type[1], chunk->type[2], chunk->type[3], dataLength); + + // Check for chunk type + if(memcmp(chunk->type, "IHDR", 4) == 0) { + // Put pointer to the right data + ihdr = (IHDRChunk*)(dataPtr + sizeof(PNGChunk)); + + // Perform byteswap for width and height values + ihdr->width = __builtin_bswap32(ihdr->width); + ihdr->height = __builtin_bswap32(ihdr->height); + + // Perform some sanity checking + if(ihdr->compression != 0) { + Log(Error, "[PNG] Compression method is not 0, can not parse this image"); + return errorImage; + } + if(ihdr->filter != 0) { + Log(Error, "[PNG] Filter method is not 0, can not parse this image"); + return errorImage; + } + + // Check if we can read this type of image file + if(ihdr->colorType != 6) { + Log(Error, "[PNG] Color type is not 6, can not parse this image"); + return errorImage; + } + if(ihdr->bits != 8) { + Log(Error, "[PNG] Bits is not 8, can not parse this image"); + return errorImage; + } + if(ihdr->interlace != 0) { + Log(Error, "[PNG] Interlace method is not 0, can not parse this image"); + return errorImage; + } + } + else if(memcmp(chunk->type, "IDAT", 4) == 0) { + // Add a pointer to this chunk of data in the list + imgDataPtrs.push_back(dataPtr + sizeof(PNGChunk)); + imgDataLens.push_back(dataLength); + } + // Check for end of chunks + else if(memcmp(chunk->type, "IEND", 4) == 0) + break; + + // Go to next chunk + dataPtr += sizeof(uint32_t) * 3 + dataLength; + } + + // Now we need to create one chuck of data for the decompressor to use + // First calculate the total lenght of data + uint32_t IDATLength = 0; + for(uint32_t len : imgDataLens) + IDATLength += len; + + // Now allocate a buffer for this complete datablock + uint8_t* IDAT = new uint8_t[IDATLength]; + + // And copy all the blocks to this buffer + uint32_t offset = 0; + for(int i = 0; i < imgDataPtrs.size(); i++) { + memcpy(IDAT + offset, imgDataPtrs[i], imgDataLens[i]); + offset += imgDataLens[i]; + } + + // Decompress image data + uint8_t* imageData = ZLIBDecompressor::Decompress(IDAT, &IDATLength); + // We don't need this anymore + delete IDAT; + + // Create resulting image + Image result = Image(ihdr->width, ihdr->height); + result.GetCanvas()->Clear(0xFFFFFFFF); + uint8_t* recon = (uint8_t*)result.GetBufferPtr(); + uint32_t reconIndex = 0; + uint32_t stride = ihdr->width * BYTES_PER_PIXEL; + + uint32_t index = 0; + for(uint32_t r = 0; r < ihdr->height; r++) + { + uint8_t filterType = imageData[index++]; + uint8_t reconX = 0; + for(uint32_t c = 0; c < stride; c++) + { + uint8_t filtX = imageData[index++]; + if(filterType == 0) + reconX = filtX; + else if(filterType == 1) + reconX = filtX + Recon_a(recon, stride, r, c); + else if(filterType == 2) + reconX = filtX + Recon_b(recon, stride, r, c); + else if(filterType == 3) + reconX = filtX + ((Recon_a(recon, stride, r, c) + Recon_b(recon, stride, r, c)) / 2); + else if(filterType == 4) + reconX = filtX + PaethPredictor(recon, Recon_a(recon, stride, r, c), Recon_b(recon, stride, r, c), Recon_c(recon, stride, r, c)); + else { + Print("[PNGDecoder] invalid filter type %d\n", filterType); + } + + recon[reconIndex] = reconX & 0xFF; + reconIndex++; + } + } + + // Convert to right pixel format + for(uint32_t i = 0; i < (result.GetHeight() * stride); i += 4) + { + uint8_t a = recon[i + 3]; + uint8_t b = recon[i + 2]; + uint8_t g = recon[i + 1]; + uint8_t r = recon[i + 0]; + + recon[i + 3] = a; + recon[i + 2] = r; + recon[i + 1] = g; + recon[i + 0] = b; + } + + return result; +} + +uint8_t PNGDecoder::PaethPredictor(uint8_t* recon, uint8_t a, uint8_t b, uint8_t c) +{ + int p = a + b - c; + int pa = Math::Abs(p - a); + int pb = Math::Abs(p - b); + int pc = Math::Abs(p - c); + uint8_t pr = 0; + + if((pa <= pb) && (pa <= pc)) + pr = a; + else if(pb <= pc) + pr = b; + else + pr = c; + + return pr; +} +uint8_t PNGDecoder::Recon_a(uint8_t* recon, uint32_t stride, uint32_t r, uint32_t c) +{ + if(c >= BYTES_PER_PIXEL) + return recon[r * stride + c - BYTES_PER_PIXEL]; + return 0; +} +uint8_t PNGDecoder::Recon_b(uint8_t* recon, uint32_t stride, uint32_t r, uint32_t c) +{ + if(r > 0) + return recon[(r-1) * stride + c]; + return 0; +} +uint8_t PNGDecoder::Recon_c(uint8_t* recon, uint32_t stride, uint32_t r, uint32_t c) +{ + if((r > 0) && (c >= BYTES_PER_PIXEL)) + return recon[(r - 1) * stride + c - BYTES_PER_PIXEL]; + return 0; +} + + + + + + + + + + + + + + + +uint8_t* ZLIBDecompressor::Decompress(uint8_t* input, uint32_t* lenOut) +{ + /* + Manely checking that this buffer is supported and valid, main function is the inflate method + */ + + BitReader* reader = new BitReader(input); + + uint8_t CMF = reader->ReadByte(); + uint8_t method = CMF & 0xF; + if(method != 8) return 0; // Only CM=8 is supported + + uint8_t CINFO = (CMF >> 4) & 0xF; + if(CINFO > 7) return 0; // CINFO must be smaller that 8 + + uint8_t FLG = reader->ReadByte(); + if(((CMF * 256 + FLG) % 31) != 0) return 0; // CMF+FLG checksum not correct + + uint8_t FDICT = (FLG >> 5) & 1; + if(FDICT != 0) return 0; // FDICT is not supported + + // Perform actual decompression + uint8_t* result = ZLIBDecompressor::Inflate(reader, lenOut); + + // Ignored for now + uint32_t adler32 = reader->ReadBytes(4); + + delete reader; + return result; +} +uint8_t* ZLIBDecompressor::Inflate(BitReader* reader, uint32_t* lenOut) +{ + uint8_t endOfBlocks = 0; + PNGBuffer buffer(ZLIB_BUFFER_SIZE); + while (!endOfBlocks) + { + endOfBlocks = reader->ReadBit(); + uint8_t blockType = reader->ReadBits(2); + + //Print("[ZLIBDecompressor] Blocktype %d\n", blockType); + if(blockType == 0) { + ZLIBDecompressor::InflateBlockNoCompression(reader, &buffer); + } + else if(blockType == 1) { + ZLIBDecompressor::InflateBlockStatic(reader, &buffer); + } + else if(blockType == 2) { + ZLIBDecompressor::InflateBlockDynamic(reader, &buffer); + } + else { + Print("[ZLIBDecompressor] Invalid blocktype %d\n", blockType); + return 0; + } + } + + if(lenOut) *lenOut = buffer.Size(); + return buffer.buffer; +} +void ZLIBDecompressor::InflateBlockNoCompression(BitReader* reader, PNGBuffer* target) +{ + uint16_t len = reader->ReadBytes(2); + uint16_t nlen = reader->ReadBytes(2); + + for(uint16_t i = 0; i < len; i++) + target->Add(reader->ReadByte()); +} +uint32_t ZLIBDecompressor::DecodeSymbol(BitReader* reader, HuffmanTree* tree) +{ + HuffmanNode* node = tree->root; + + while(node->left || node->right) { + uint8_t b = reader->ReadBit(); + if(b) node = node->right; + else node = node->left; + } + //Print("[ZLIBDecompressor] node is %x!\n", (uint32_t)node); + return node->symbol; +} +void ZLIBDecompressor::InflateBlockData(BitReader* reader, HuffmanTree* literalLengthTree, HuffmanTree* distanceTree, PNGBuffer* target) +{ + while(true) + { + uint32_t sym = DecodeSymbol(reader, literalLengthTree); + if (sym <= 255) { // Literal byte + target->Add(sym & 0xFF); + } + else if(sym == 256) { // End of block + return; + } + else { // pair + sym -= 257; + uint32_t length = reader->ReadBits(lengthExtraBits[sym]) + lengthBase[sym]; + uint32_t distSym = DecodeSymbol(reader, distanceTree); + uint32_t dist = reader->ReadBits(distanceExtraBits[distSym]) + distanceBase[distSym]; + + //memcpy(&target->buffer[target->Size()-1], &target->buffer[target->index - dist], length); + //target->index += length; + + dist = target->Size() - dist; + for(uint32_t i = 0; i < length; i++, dist++) { + target->Add(target->Get(dist)); + } + // out.append(out[-dist]) + } + } +} +HuffmanTree* ZLIBDecompressor::BitListToTree(List* bitList, List* alphabet) +{ + uint32_t maxBits = 0; + for(uint32_t d : *bitList) + if(d > maxBits) + maxBits = d; + + uint32_t* blCount = new uint32_t[(maxBits+1)]; + memset(blCount, 0, (maxBits + 1) * sizeof(uint32_t)); + for(uint32_t i = 0; i < (maxBits+1); i++) { + // Count occurencies in bitList + for(uint32_t item : *bitList) + if(i == item && item != 0) + blCount[i] += 1; + } + + uint32_t* nextCode = new uint32_t[(maxBits+1)]; + memset(nextCode, 0, (maxBits + 1) * sizeof(uint32_t)); + nextCode[0] = 0; + nextCode[1] = 0; + for(uint32_t bits = 2; bits < (maxBits+1); bits++) { + nextCode[bits] = ((nextCode[bits-1] + blCount[bits-1]) << 1); + } + + HuffmanTree* result = new HuffmanTree(); + for(int i = 0; i < alphabet->size(); i++) { + uint32_t c = i; //(i < alphabet->size()) ? alphabet->GetAt(i) : 0; + uint32_t bitLen = (i < bitList->size()) ? bitList->GetAt(i) : 0; + if(bitLen) { + result->Insert(nextCode[bitLen], bitLen, c); + nextCode[bitLen] += 1; + } + } + delete blCount; + delete nextCode; + return result; +} +DecodeTreesResult ZLIBDecompressor::DecodeTrees(BitReader* reader) +{ + // The number of literal/length codes + uint32_t HLIT = reader->ReadBits(5) + 257; + + // The number of distance codes + uint32_t HDIST = reader->ReadBits(5) + 1; + + // The number of code length codes + uint32_t HCLEN = reader->ReadBits(4) + 4; + + // Create result container + DecodeTreesResult result; + + // Read code lengths for the code length alphabet + uint32_t codeLengthTreeBitList[19]; + for(int i = 0; i < 19; i++) + codeLengthTreeBitList[i] = 0; + + for(uint32_t i = 0; i < HCLEN; i++) + codeLengthTreeBitList[codeLengthCodesOrder[i]] = reader->ReadBits(3); + + List codeLengthBitList; + List codeLengthAlphaList; + for(int i = 0; i < 19; i++) { + codeLengthBitList.push_back(codeLengthTreeBitList[i]); + codeLengthAlphaList.push_back(i); + } + + // Construct code length tree + HuffmanTree* codeLengthTree = BitListToTree(&codeLengthBitList, &codeLengthAlphaList); + + // Read literal/length + distance code length list + List bl; + while (bl.size() < HLIT + HDIST) + { + uint32_t sym = DecodeSymbol(reader, codeLengthTree); + if((sym >= 0) && (sym <= 15)) { // literal value + bl.push_back(sym); + } + else if(sym == 16) { + // copy the previous code length 3..6 times. + // the next 2 bits indicate repeat length ( 0 = 3, ..., 3 = 6 ) + uint32_t prev_code_length = bl.GetAt(bl.size() - 1); + uint32_t repeat_length = reader->ReadBits(2) + 3; + for(uint32_t i = 0; i < repeat_length; i++) + bl.push_back(prev_code_length); + } + else if(sym == 17) { + // repeat code length 0 for 3..10 times. (3 bits of length) + uint32_t repeat_length = reader->ReadBits(3) + 3; + for(uint32_t i = 0; i < repeat_length; i++) + bl.push_back(0); + } + else if(sym == 18) { + // repeat code length 0 for 11..138 times. (7 bits of length) + uint32_t repeat_length = reader->ReadBits(7) + 11; + for(uint32_t i = 0; i < repeat_length; i++) + bl.push_back(0); + } + else + Log(Error, "[ZLIBDecompressor] Invalid symbol"); + } + delete codeLengthTree; + + // Construct trees + List blTemp; + List alphaTemp; + for(uint32_t i = 0; i < HLIT; i++) + blTemp.push_back(bl[i]); + + for(int i = 0; i < 286; i++) + alphaTemp.push_back(i); + + // Create first tree for the literal lengths + result.literalLengthTree = BitListToTree(&blTemp, &alphaTemp); + + // Reset lists for re-use + blTemp.Clear(); + alphaTemp.Clear(); + + for(int i = HLIT; i < bl.size(); i++) + blTemp.push_back(bl[i]); + + for(int i = 0; i < 30; i++) + alphaTemp.push_back(i); + + // Create second list + result.distanceTree = BitListToTree(&blTemp, &alphaTemp); + + // And exit function + return result; +} +void ZLIBDecompressor::InflateBlockDynamic(BitReader* reader, PNGBuffer* target) +{ + DecodeTreesResult ret = ZLIBDecompressor::DecodeTrees(reader); + //printBT(ret.literalLengthTree->root); + //printBT(ret.distanceTree->root); + InflateBlockData(reader, ret.literalLengthTree, ret.distanceTree, target); + delete ret.literalLengthTree; + delete ret.distanceTree; +} + +void ZLIBDecompressor::InflateBlockStatic(BitReader* reader, PNGBuffer* target) +{ + static HuffmanTree* literalLengthTree = 0; + static HuffmanTree* distanceTree = 0; + + if(literalLengthTree == 0) { + List bl1; + + List alphaTemp; + for(int i = 0; i < 286; i++) + alphaTemp.push_back(i); + + for(int i = 0; i < 144; i++) + bl1.push_back(8); + for(int i = 144; i < 256; i++) + bl1.push_back(9); + for(int i = 256; i < 280; i++) + bl1.push_back(7); + for(int i = 280; i < 288; i++) + bl1.push_back(8); + + literalLengthTree = BitListToTree(&bl1, &alphaTemp); + //printBT(literalLengthTree->root); + + bl1.Clear(); + alphaTemp.Clear(); + for(int i = 0; i < 30; i++) { + bl1.push_back(5); + alphaTemp.push_back(i); + } + + distanceTree = BitListToTree(&bl1, &alphaTemp); + //printBT(distanceTree->root); + } + + ZLIBDecompressor::InflateBlockData(reader, literalLengthTree, distanceTree, target); +} \ No newline at end of file diff --git a/lib/src/vector.cpp b/lib/src/vector.cpp new file mode 100644 index 0000000..587f167 --- /dev/null +++ b/lib/src/vector.cpp @@ -0,0 +1,76 @@ +#include +#include + +using namespace LIBCactusOS; + +template +void Vector::reserve(int capacity) +{ + T* newBuf = new T[capacity]; + memcpy(newBuf, this->buffer, sizeof(T) * this->size); + + this->capacity = capacity; + + delete this->buffer; + this->buffer = newBuf; +} + +template +Vector::Vector() +{ + this->size = 0; + this->capacity = 0; + this->buffer = 0; +} + +template +int Vector::Size() +{ + return this->size; +} + +template +void Vector::push_back(const T& item) +{ + if(this->capacity == 0) + reserve(10); + else if(this->size == this->capacity) + reserve(2 * this->size); + + this->buffer[this->size] = item; + this->size++; +} + +template +void Vector::pop_back() +{ + this->size--; +} + +template +void Vector::clear() +{ + this->capacity = 0; + this->size = 0; + + delete this->buffer; + this->buffer = 0; +} + +template +T& Vector::GetAt(int n) +{ + return this->buffer[n]; +} + +template +T& Vector::operator[](int n) +{ + return this->buffer[n]; +} + +template +T* Vector::data() +{ + return this->buffer; +} \ No newline at end of file