/*****************************************************************************/
/* CascRootFile_Text.cpp                  Copyright (c) Ladislav Zezula 2017 */
/*---------------------------------------------------------------------------*/
/* Support for generic ROOT handler with mapping of FileName -> CKey         */
/*---------------------------------------------------------------------------*/
/*   Date    Ver   Who  Comment                                              */
/* --------  ----  ---  -------                                              */
/* 28.10.15  1.00  Lad  The first version of CascRootFile_Text.cpp           */
/*****************************************************************************/

#define __CASCLIB_SELF__
#include "CascLib.h"
#include "CascCommon.h"

//-----------------------------------------------------------------------------
// Handler definitions for Starcraft I root file

struct TRootHandler_SC1 : public TFileTreeRoot
{
    public:

    TRootHandler_SC1() : TFileTreeRoot(0)
    {
        // We have file names and return CKey as result of search
        dwFeatures |= (CASC_FEATURE_FILE_NAMES | CASC_FEATURE_ROOT_CKEY);
    }

    static bool IsRootFile(LPBYTE pbRootFile, size_t cbRootFile)
    {
        CASC_CSV Csv(1, false);
        size_t nColumns;
        bool bResult = false;

        // Get the first line from the listfile
        if(Csv.Load(pbRootFile, cbRootFile) == ERROR_SUCCESS)
        {
            // There must be 2 or 3 elements
            nColumns = Csv[CSV_ZERO].GetColumnCount();
            if(nColumns == 2 || nColumns == 3)
            {
                const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][CSV_ZERO];
                const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][1];

                bResult = (FileName.szValue && CKeyStr.szValue && CKeyStr.nLength == MD5_STRING_SIZE);
            }
        }

        // We need to reset the listfile to the begin position
        return bResult;
    }

    DWORD Load(TCascStorage * hs, LPBYTE pbRootFile, size_t cbRootFile)
    {
        PCASC_CKEY_ENTRY pCKeyEntry;
        CASC_CSV Csv(0, false);
        BYTE CKey[MD5_HASH_SIZE];
        DWORD dwErrCode;

        // Parse the ROOT file first in order to see whether we have the correct format
        dwErrCode = Csv.Load(pbRootFile, cbRootFile);
        if(dwErrCode == ERROR_SUCCESS)
        {
            // Parse all lines
            while(Csv.LoadNextLine())
            {
                const CASC_CSV_COLUMN & FileName = Csv[CSV_ZERO][CSV_ZERO];
                const CASC_CSV_COLUMN & CKeyStr = Csv[CSV_ZERO][1];

                // Convert the CKey to binary
                if(BinaryFromString(CKeyStr.szValue, MD5_STRING_SIZE, CKey) == ERROR_SUCCESS)
                {
                    // Verify whether it is a known entry
                    if((pCKeyEntry = FindCKeyEntry_CKey(hs, CKey)) != NULL)
                    {
                        // Insert the FileName+CKey to the file tree
                        FileTree.InsertByName(pCKeyEntry, FileName.szValue);
                    }
                }
            }
        }

        return dwErrCode;
    }
};

//-----------------------------------------------------------------------------
// Public functions

//
// Starcraft ROOT file is a text file with the following format:
// HD2/portraits/NBluCrit/NLCFID01.webm|c2795b120592355d45eba9cdc37f691e
// locales/enUS/Assets/campaign/EXPZerg/Zerg08/staredit/wav/zovtra01.ogg|316b0274bf2dabaa8db60c3ff1270c85
// locales/zhCN/Assets/sound/terran/ghost/tghdth01.wav|6637ed776bd22089e083b8b0b2c0374c
//

DWORD RootHandler_CreateStarcraft1(TCascStorage * hs, CASC_BLOB & RootFile)
{
    TRootHandler_SC1 * pRootHandler = NULL;
    DWORD dwErrCode = ERROR_BAD_FORMAT;

    // Verify whether this looks like a Starcraft I root file
    if(TRootHandler_SC1::IsRootFile(RootFile.pbData, RootFile.cbData))
    {
        // Allocate the root handler object
        pRootHandler = new TRootHandler_SC1();
        if(pRootHandler != NULL)
        {
            // Load the root directory. If load failed, we free the object
            dwErrCode = pRootHandler->Load(hs, RootFile.pbData, RootFile.cbData);
            if(dwErrCode != ERROR_SUCCESS)
            {
                delete pRootHandler;
                pRootHandler = NULL;
            }
        }
    }

    // Assign the root directory (or NULL) and return error
    hs->pRootHandler = pRootHandler;
    return dwErrCode;
}
