From 7712398e0c6cf25db29bb6b228399e5ca82bbe08 Mon Sep 17 00:00:00 2001 From: "JASON-6700K\\jandersen" Date: Sat, 18 Jul 2020 15:49:27 -0400 Subject: [PATCH] GSLA encoder: WIP --- source/gsla_file.cpp | 197 ++++++++++++++++++++++++++++++++++++++++++- source/gsla_file.h | 6 +- source/lzb.cpp | 16 ++++ source/lzb.h | 6 ++ source/main.cpp | 28 +++++- 5 files changed, 246 insertions(+), 7 deletions(-) diff --git a/source/gsla_file.cpp b/source/gsla_file.cpp index 5f90fad..159274c 100644 --- a/source/gsla_file.cpp +++ b/source/gsla_file.cpp @@ -114,17 +114,21 @@ // // xxx_xxxx_xxxx_xxx is the number of bytes 1-16384 to follow (0 == 1 byte) // -//%0xxx_xxxx_xxxx_xxx0 - Copy Bytes - straight copy bytes +//%0xxx_xxxx_xxxx_xxx1 - Copy Bytes - straight copy bytes //%1xxx_xxxx_xxxx_xxx1 - Skip Bytes - skip bytes / move the cursor //%1xxx_xxxx_xxxx_xxx0 - Dictionary Copy Bytes from frame buffer to frame buffer // -//%0000_0000_0000_0001- Source Skip -> Source pointer skips to next bank of data -//%0000_0000_0000_0011- End of Frame - end of frame -//%0000_0000_0000_0111- End of Animation / End of File / no more frames +//%0000_0000_0000_0000- Source Skip -> Source pointer skips to next bank of data +//%0000_0000_0000_0010- End of Frame - end of frame +//%0000_0000_0000_0110- End of Animation / End of File / no more frames +// // // other remaining codes, are reserved for future expansion #include "gsla_file.h" + +#include "lzb.h" + #include // If these structs are the wrong size, there's an issue with type sizes, and @@ -143,6 +147,17 @@ GSLAFile::GSLAFile(const char *pFilePath) { LoadFromFile(pFilePath); } + +//------------------------------------------------------------------------------ + +GSLAFile::GSLAFile(int iWidthPixels, int iHeightPixels, int iFrameSizeBytes ) + : m_widthPixels(iWidthPixels) + , m_heightPixels(iHeightPixels) + , m_frameSize( iFrameSizeBytes ) +{ + +} + //------------------------------------------------------------------------------ GSLAFile::~GSLAFile() @@ -270,3 +285,177 @@ void GSLAFile::UnpackAnimation(GSLA_ANIM* pANIM, GSLA_Header* pHeader) //DecompressAnim(pTargetBuffer, pData); } +//------------------------------------------------------------------------------ +// +// Append a copy of raw image data into the class +// +void GSLA::AddImages( const std::vector& pFrameBytes ) +{ + for (int idx = 0; idx < pFrameBytes.size(); ++idx) + { + unsigned char* pPixels = new unsigned char[ m_frameSize ]; + memcpy(pPixels, pFrameBytes[ idx ], m_frameSize ); + m_pC1PixelMaps.push_back( pPixels ); + } +} + +//------------------------------------------------------------------------------ +// +// Compress / Serialize a new GSLA File +// +void GSLA::SaveToFile(const char* pFilenamePath) +{ + // We're not going to even try encoding an empty file + if (m_pC1PixelMaps.size() < 1) + { + return; + } + + // serialize to memory, then save that to a file + std::vector bytes; + + //-------------------------------------------------------------------------- + // Add the header + bytes.resize( bytes.size() + sizeof(GSLA_Header) ); + + //$$JGA Rememeber, you have to set the pointer, before every access + //$$JGA to the header data, because vector is going to change out + //$$JGA memory addresses from underneath you + GSLA_Header* pHeader = (GSLA_Header*)&bytes[0]; + + pHeader->G = 'G'; pHeader->S = 'S'; pHeader->L = 'L'; pHeader->A = 'A'; + + pHeader->file_length = 0; // Temp File Length + + pHeader->version = 0x8000; // Version 0, with a Ring/Loop Frame at the end + + pHeader->width = m_widthPixels >> 1; + pHeader->height = m_widthHeight; + + pHeader->frame_size = m_frameSize; + + pHeader->frame_count = m_pC1PixelMaps.size() + 1; // + 1 for the ring frame + + //-------------------------------------------------------------------------- + // Add the INITial frame chunk + // + // If there's only an initial frame, I guess this becomes a picture + // + + size_t init_offset = bytes.size(); + + // Add space for the INIT header + bytes.resize( bytes.size() + sizeof(GSLA_INIT) ); + GSLA_INIT* pINIT = (GSLA_INIT*) &bytes[ init_offset ]; + + pINIT->I = 'I'; pINIT->N = 'N'; pINIT->i = 'I'; pINIT->T = 'T'; + pINIT->chunk_length = 0; // temp chunk size + + // Need a place to put compressed data, in theory it could be bigger + // than the original data, I think if that happens, the image was probably + // designed to break this, anyway, give double theoretical max + unsigned char* pWorkBuffer = new unsigned char[ m_frameSize * 2 ]; + + unsigned char* pInitialFrame = m_pC1PixelMaps[ 0 ]; + + // We're not worried about bank wrap on the first frame, and we don't have a pre-populated + // dictionary + int compressedSize = LZBA_Compress(pWorkBuffer, pInitialFrame, m_frameSize, + pInitialFrame, pInitialFrame); + + for (int compressedIndex = 0; compressedIndex < compressedSize; ++compressedIndex) + { + bytes.push_back(pWorkBuffer[ compressedIndex ]); + } + + // Insert EOF/ End of Animation Done opcode + bytes.push_back( 0x06 ); + bytes.push_back( 0x00 ); + + + // Reset pointer to the pINIT (as the baggage may have shifted) + pINIT = (GSLA_INIT*) &bytes[ init_offset ]; + pINIT->chunk_length = (unsigned int) (bytes.size() - init_offset); + + //-------------------------------------------------------------------------- + // Add the ANIMation frames chunk + // + // We always add this, because we always add a Ring/Loop frame, we always + // end up with at least 2 frames + // + + size_t anim_offset = bytes.size(); + + // Add Space for the ANIM Header + bytes.resize( bytes.size() + sizeof(GLSA_ANIM) ); + GSLA_ANIM* pANIM = (GSLA_ANIM*) &bytes[ anim_offset ]; + + pANIM->A = 'A'; pANIM->N = 'N'; pANIM->I ='I'; pANIM->M = 'M'; + pANIM->chunk_length = 0; // temporary chunk size + + // Initialize the Canvas with the initial frame (we alread exported this) + unsigned char *pCanvas = new unsigned char[ m_frameSize ]; + memcpy(pCanvas, m_pC1PixelMaps[0], m_frameSize); + + memcpy(pCanvas, m_pC1PixelMaps[0], m_frameSize); + + // Let's encode some frames buddy + for (int frameIndex = 1; frameIndex < m_pC1PixelMaps.size(); ++frameIndex) + { + // I don't want random data in the bank gaps, so initialize this + // buffer with zero + memset(pWorkBuffer, 0, m_frameSize * 2); + + int frameSize = LZBA_Compress(pWorkBuffer, m_pC1PixelMaps[ frameIndex], + m_frameSize, pWorkBuffer-bytes.size(), + pCanvas, m_frameSize ); + + for (int frameIndex = 0; frameIndex < frameSize; ++frameIndex) + { + bytes.push_back(pWorkBuffer[ frameIndex ]); + } + } + + // Add the RING Frame + memset(pWorkBuffer, 0, m_frameSize * 2); + + int ringSize = LZBA_Compress(pWorkBuffer, m_pC1PixelMaps[ 0 ], + m_frameSize, pWorkBuffer-bytes.size(), + pCanvas, m_frameSize ); + + for (int ringIndex = 0; ringIndex < ringSize; ++ringIndex) + { + bytes.push_back(pWorkBuffer[ ringIndex ]); + } + + delete[] pCanvas; pCanvas = nullptr; + + // Insert End of file/ End of Animation Done opcode + bytes.push_back( 0x06 ); + bytes.push_back( 0x00 ); + + // Update the chunk length + pFRAM = (FanFile_FRAM*)&bytes[ fram_offset ]; + pFRAM->chunk_length = (unsigned int) (bytes.size() - fram_offset); + + // Update the header + pHeader = (FanFile_Header*)&bytes[0]; // Required + pHeader->file_length = (unsigned int)bytes.size(); // get some valid data in there + + // Try not to leak memory, even though we probably do + delete[] pWorkBuffer; + + //-------------------------------------------------------------------------- + // Create the file and write it + FILE* pFile = nullptr; + errno_t err = fopen_s(&pFile, pFilenamePath, "wb"); + + if (0==err) + { + fwrite(&bytes[0], sizeof(unsigned char), bytes.size(), pFile); + fclose(pFile); + } +} + +//------------------------------------------------------------------------------ + diff --git a/source/gsla_file.h b/source/gsla_file.h index 3c4e597..70bf6aa 100644 --- a/source/gsla_file.h +++ b/source/gsla_file.h @@ -112,9 +112,13 @@ class GSLAFile public: // Load in a C2 File GSLAFile(const char *pFilePath); - ~GSLAFile(); + // Creation + GSLAFile(int iWidthPixels, int iHeightPixels, int iFrameSizeBytes); + void AddImages( const std::vector& pFrameBytes ); + void SaveToFile(const char* pFilenamePath); + // Retrieval void LoadFromFile(const char* pFilePath); int GetFrameCount() { return (int)m_pC1PixelMaps.size(); } diff --git a/source/lzb.cpp b/source/lzb.cpp index 72c17c3..544aa2e 100644 --- a/source/lzb.cpp +++ b/source/lzb.cpp @@ -574,4 +574,20 @@ void LZB_Decompress(unsigned char* pDest, unsigned char* pSource, int destSize) } //------------------------------------------------------------------------------ +// +// Encode a Frame in GSLA LZB Format +// +int LZBA_Compress(unsigned char* pDest, unsigned char* pSource, int sourceSize, unsigned char* pDataStart, int dictionarySize) +{ +} + +//------------------------------------------------------------------------------ +// +// Decompress a Frame in the GSLA LZB Format +// +int LZBA_Decompress(unsigned char* pDest, unsigned char* pSource, unsigned char* pDataStart) +{ +} + +//------------------------------------------------------------------------------ diff --git a/source/lzb.h b/source/lzb.h index 015ba4b..d8d624e 100644 --- a/source/lzb.h +++ b/source/lzb.h @@ -11,5 +11,11 @@ int LZB_Compress(unsigned char* pDest, unsigned char* pSource, int sourceSize); int Old_LZB_Compress(unsigned char* pDest, unsigned char* pSource, int sourceSize); void LZB_Decompress(unsigned char* pDest, unsigned char* pSource, int destSize); +// +// LZB Compressor that uses GSLA Opcodes while encoding +// +int LZBA_Compress(unsigned char* pDest, unsigned char* pSource, int sourceSize, unsigned char* pDataStart, unsigned char* pDictionary, int dictionarySize=0); +int LZBA_Decompress(unsigned char* pDest, unsigned char* pSource, unsigned char* pDataStart); + #endif // LZB_H diff --git a/source/main.cpp b/source/main.cpp index a7325d1..699dada 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,5 +1,7 @@ // // GSLA - GS LZB Animation Tool +// +// Look in gsla_file.cpp/h for more information about the file format // #include @@ -7,16 +9,20 @@ #include #include "c2_file.h" +#include "gsla_file.h" + #include "lzb.h" //------------------------------------------------------------------------------ static void helpText() { - printf("GSLA - v0.0\n"); + printf("GSLA - v1.0\n"); printf("--------------\n"); - printf("GS Lzb Animation Tool\n"); + printf("GS Lzb Animation Creation Tool\n"); printf("\n"); printf("\ngsla [options] \n"); + printf("\n\n There are no [options] yet\n"); + printf("Converts from C2 to GSLA\n"); exit(-1); } @@ -111,8 +117,25 @@ int main(int argc, char* argv[]) helpText(); } + if (pOutfilePath) + { + const std::vector& c1Datas = c2data.GetPixelMaps(); + + printf("Saving %s with %d frames\n", pOutfilePath, (int)c1Datas.size()); + + GSLAFile anim(320,200, 0x8000); + + anim.AddImages(c1Datas); + + anim.SaveToFile(pOutfilePath); + + } + + #if 0 const std::vector& c1Datas = c2data.GetPixelMaps(); + // LZB Testing Code, that I should probalby remove from this file + // Just ignore this block of code #if 1 unsigned char workbuffer[64*1024]; for (int idx = 0; idx < frameCount; ++idx) @@ -160,6 +183,7 @@ int main(int argc, char* argv[]) } #endif + #endif } }