/*---------------------------------------------------------------------------*/
/* zip.cpp                                                                   */
/* copyright (c) innovaphone 2021                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>
#include "../ilib/crc.h"
#include "../ilib/zip.h"

USING_NAMESPACE

Zip::Zip()
{
    buf = 0;
    tmp = 0;
    pos = 0;
    tmpPos = 0;
    count = 0;
}

void Zip::Init(byte * buffer, byte * temp)
{
    buf = buffer;
    tmp = temp;
    pos = 0;
    tmpPos = 0;
    count = 0;
}

void Zip::AddFile(const char * filename, const byte * data, unsigned len)
{
    ASSERT(buf && tmp, "Zip::Add - not initialized");
    ASSERT(filename && data, "Zip::Add - mandatory parameters missing");
    
    dword crc = 0;
    crc = Crc::crc32(crc, data, len);
    count++;

    tmpPos += Zip::WriteCentralDirectoryHeader(tmp + tmpPos, filename, len, crc, pos);
    pos += Zip::WriteLocalFileHeader(buf + pos, filename, len, crc);
    memcpy(buf + pos, data, len);
    pos += len;
}

unsigned Zip::Final()
{
    ASSERT(buf, "Zip::Final - not initialized");

    dword cdhOffset = pos;
    memcpy(buf + pos, tmp, tmpPos);
    pos += tmpPos;
    pos += Zip::WriteEndOfCentralDirectory(buf + pos, count, tmpPos, cdhOffset);

    buf = 0;
    tmp = 0;
    return pos;
}

static void add_dword(byte * buf, unsigned & pos, dword value)
{
    WRITE_DWORD_LE(buf + pos, value);
    pos += 4;
}

static void add_word(byte * buf, unsigned & pos, word value)
{
    WRITE_WORD_LE(buf + pos, value);
    pos += 2;
}

unsigned Zip::WriteLocalFileHeader(byte * out, const char * fileName, unsigned fileSize, dword crc)
{
    dword fileNameLen = (dword)strlen(fileName);
    byte * buf = out;
    dword pos = 0;

    // Local file header
    add_dword(buf, pos, 0x04034b50);		            // Local file header signature 
    add_word(buf, pos, 0x14);	  		                // Version needed to extract
    add_word(buf, pos, 0);					            // General purpose bit flag
    add_word(buf, pos, 0);                              // Compression method (no compression)
    add_word(buf, pos, 0);                              // File last modification time
    add_word(buf, pos, 0);                              // File last modification date
    add_dword(buf, pos, crc);                           // CRC
    add_dword(buf, pos, fileSize);                      // Compressed size
    add_dword(buf, pos, fileSize);                      // Uncompressed size
    add_word(buf, pos, fileNameLen);                    // File name size
    add_word(buf, pos, 0);                              // Extra field size
    memcpy(buf + pos, fileName, fileNameLen);           // Filename
    pos += fileNameLen;

    return pos;
}

unsigned Zip::WriteCentralDirectoryHeader(byte * out, const char * fileName, unsigned fileSize, dword crc, unsigned localHeaderOffset)
{
    dword fileNameLen = (dword)strlen(fileName);
    dword tmpPos = 0;
    byte * tmp = out;

    // Central directory header
    add_dword(tmp, tmpPos, 0x02014b50);                 // Central directory header signature
    add_word(tmp, tmpPos, 0x14);                        // Version made by
    add_word(tmp, tmpPos, 0x14);                        // Version needed to extract
    add_word(tmp, tmpPos, 0);					        // General purpose bit flag
    add_word(tmp, tmpPos, 0);                           // Compression method (no compression)
    add_word(tmp, tmpPos, 0);                           // File last modification time
    add_word(tmp, tmpPos, 0);                           // File last modification date
    add_dword(tmp, tmpPos, crc);                        // CRC
    add_dword(tmp, tmpPos, fileSize);                   // Compressed size
    add_dword(tmp, tmpPos, fileSize);                   // Uncompressed size
    add_word(tmp, tmpPos, fileNameLen);                 // File name size
    add_word(tmp, tmpPos, 0);                           // Extra field size
    add_word(tmp, tmpPos, 0);                           // File comment size
    add_word(tmp, tmpPos, 0);                           // Disk number start
    add_word(tmp, tmpPos, 0);                           // Internal file attributes
    add_dword(tmp, tmpPos, 0x20);                       // External file attributes
    add_dword(tmp, tmpPos, localHeaderOffset);          // Local header offset
    memcpy(tmp + tmpPos, fileName, fileNameLen);        // Filename
    tmpPos += fileNameLen;

    return tmpPos;
}

unsigned Zip::WriteEndOfCentralDirectory(byte * out, unsigned fileCount, unsigned centralDirectoryHeaderSize, unsigned centralDirectoryHeaderOffset)
{
    byte * buf = out;
    unsigned pos = 0;

    // End of central directory
    add_dword(buf, pos, 0x06054b50);                    // End of central directory signatue
    add_word(buf, pos, 0);                              // Number of this disk
    add_word(buf, pos, 0);                              // Number of the disk with the start of the central directory
    add_word(buf, pos, fileCount);                      // Total number of entries on the disk
    add_word(buf, pos, fileCount);                      // Total number of entries in the central directory
    add_dword(buf, pos, centralDirectoryHeaderSize);    // CDH size
    add_dword(buf, pos, centralDirectoryHeaderOffset);  // CDH offset
    add_word(buf, pos, 0);                              // File comment size

    return pos;
}
