Subversion Repositories spk

Rev

Rev 322 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <spk.h>
#include "Utils/CommandLine.h"

void PrintError ( int err )
{
        switch ( err )
        {
                case CATERR_NODATFILE:
                        printf ( "No dat file found\n" );
                        break;
                case CATERR_NOCATFILE:
                        printf ( "Unable to open cat file\n" );
                        break;
                case CATERR_FILEEMPTY:
                        printf ( "Cat file is empty\n" );
                        break;
                case CATERR_READCAT:
                        printf ( "Unable to read cat file\n" );
                        break;
                case CATERR_DECRYPT:
                        printf ( "Unable to decrypt cat file\n" );
                        break;
                case CATERR_MISMATCH:
                        printf("Dat file size mismatch\n");
                        break;
                case CATERR_CREATED:
                        printf("Cat file does not exist\n");
                        break;
                default:
                        printf("Unknown error code: %d\n", err);
                        break;
        }
}

void ListFiles(const Utils::CommandLine &cmd)
{
        const Utils::WString& filename = cmd.arg(1);
        const Utils::WString& searchmask = cmd.arg(2);

        CFileIO File(!filename.contains(L":") ? CDirIO(cmd.cmdDir()).file(filename) : filename);
        if(!File.exists())
                File.open(filename);
        if (!File.exists())
        {
                wprintf(L"Error: Cat file: %s, does not exist\n", filename.c_str());
                return;
        }
        
        wprintf(L"Listing files in %s...", filename.c_str());
        if (!searchmask.empty())
                wprintf(L"(%s)", searchmask.c_str() );
        printf("\n");

        CCatFile catfile;
        int err = catfile.open(File.fullFilename(), L"", CATREAD_CATDECRYPT);

        int listed = 0;
        if ( err == CATERR_NONE )
        {
                wprintf(L"Opened file\n" );
                for (size_t i = 0; i < catfile.GetNumFiles(); i++)
                {
                        SInCatFile *file = catfile.GetFile ( i );
                        if (file)
                        {
                                if (!searchmask.empty())
                                {
                                        if (!file->sFile.match(searchmask))
                                                continue;
                                }

                                wprintf(L"[%9s] %s\n", SPK::GetSizeString(static_cast<unsigned long>(file->lSize)).c_str(), file->sFile.c_str());
                                ++listed;
                        }
                }

                if (!searchmask.empty())
                        wprintf(L"\nListed Files: [%d/%d]", listed, static_cast<int>(catfile.GetNumFiles()));
        }
        else
                PrintError ( err );
}

void findAllFiles(const CDirIO &dirIO, const Utils::WString& dir, const Utils::WString& filepattern, Utils::WStringList& list)
{
        Utils::WStringList dirList;
        if (dirIO.dirList(dirList, dir, filepattern, true))
        {
                for (const auto& entry : dirList)
                {
                        if (dirIO.isDir(entry->str))
                                findAllFiles(dirIO, entry->str, filepattern, list);
                        else
                                list.pushBack(entry->str);
                }
        }
}

bool findFiles(Utils::WStringList &files, const Utils::WString &filepattern, const Utils::CommandLine &cmd)
{
        CFileIO File((filepattern.contains(L":")) ? filepattern : cmd.cmdDir() + L"/" + filepattern);
        return File.GetDirIO().dirList(files, Utils::WString::Null(), File.filename(), true);
}


void ExtractFile (const Utils::CommandLine& cmd, const Utils::WString& fileMask, bool preserve)
{
        Utils::WString filename = cmd.arg(1) + fileMask;
        Utils::WString to = cmd.argCount() < 2 ? cmd.cmdDir() : cmd.arg(2);
        if (!to.contains(L":")) to = cmd.file()->dirIO().file(to);
        CDirIO destination(to);

        if (cmd.hasSwitch("preserve"))
                preserve = true;
                
        if ( !filename.contains(L"::") ) return;

        Utils::WString catfile = filename.token(L"::", 1);
        CFileIO File((catfile.contains(L":")) ? catfile : cmd.cmdDir() + L"/" + catfile);
        Utils::WString filemask = filename.token(L"::", 2);

        CCatFile cat;
        int err = cat.open(File.fullFilename(), L"", CATREAD_DAT);
        if ( err )
        {
                PrintError ( err );
                return;
        }

        Utils::WStringList fileList;
        if (filemask.containsAny(L"*?"))
        {
                for (size_t i = 0; i < cat.GetNumFiles(); i++)
                {
                        SInCatFile* f = cat.GetFile(i);
                        if(f && f->sFile.match(filemask))
                                fileList.pushBack(f->sFile);
                }
        }
        else
                fileList.pushBack(filemask);

        if (fileList.empty())
        {
                wprintf(L"Error: unable to find any files matching: %s\n", filemask.c_str() );
                return;
        }

        for (auto itr = fileList.begin(); itr != fileList.end(); itr++)
        {
                Utils::WString file = (*itr)->str;
                if (!cat.extractFile(file, to, preserve))
                        wprintf(L"Error: %s\n", cat.getErrorString().c_str() );
                else
                        wprintf(L"File has been written: %s\n", preserve ? file.c_str() : CFileIO(file).filename().c_str());
        }
}

void AppendFile (const Utils::CommandLine& cmd)
{
        Utils::WString sFile = cmd.arg(2);
        Utils::WString filepattern = cmd.arg(1);
        Utils::WString catfile;
        Utils::WString file;

        bool recursive = cmd.hasSwitch(L"recursive");

        Utils::WString C_File = sFile.findReplace(L"\\", L"/");
        bool doFile = false;
        if ( !C_File.contains(L"::"))
        {
                catfile = C_File;
                doFile = true;
        }
        else
        {
                catfile = C_File.token(L"::", 1);
                file = C_File.token(L"::", 2);
        }

        if (!catfile.contains(":"))
                catfile = cmd.cmdDir() + L"/" + catfile;

        Utils::WStringList list;

        CFileIO File((filepattern.contains(L":")) ? filepattern : cmd.cmdDir() + L"/" + filepattern);
        if (recursive)
                findAllFiles(File.dirIO(), L"", File.filename(), list);
        else
                findFiles(list, filepattern, cmd);
        if (!list.size())
        {
                wprintf(L"Error: no files found to add: %s\n", filepattern.c_str());
                return;
        }

        CCatFile cat;
        int err = cat.open(catfile, L"", CATREAD_CATDECRYPT);
        if ( err && err != CATERR_CREATED)
        {
                PrintError ( err );
                return;
        }


        int added = 0;
        for (auto itr = list.begin(); itr != list.end(); itr++)
        {
                if (File.dirIO().isDir((*itr)->str))
                        continue;

                Utils::WString filename = (*itr)->str;
                Utils::WString toFile = file;
                if (doFile)                     
                        toFile = File.dirIO().relativePath(filename);;

                if (!toFile.contains(L"."))
                {
                        if (toFile[static_cast<unsigned long>(toFile.length() - 1)] != L'/')
                                toFile += L"/";
                        toFile += CFileIO(filename).filename();
                }

                ++added;
                if (cat.appendFile(filename, toFile))
                        wprintf(L"File %s has beed added to: %s::%s\n", filename.c_str(), catfile.c_str(), toFile.c_str());
                else
                        wprintf(L"Error: Unable to add file: %s\n", filename.c_str());
        }

        if(!added)
                wprintf(L"Warning: Nothing to add: %s\n", filepattern.c_str());
}

void RemoveFile (const Utils::CommandLine &cmd)
{
        Utils::WString C_File = cmd.arg(1);
        Utils::WString remfile = cmd.arg(2);

        CFileIO File((C_File.contains(L":")) ? C_File : cmd.cmdDir() + L"/" + C_File);
        // first open the cat file
        CCatFile cat;
        int err = cat.open(File.fullFilename(), L"", CATREAD_CATDECRYPT);
        if ( err )
        {
                PrintError ( err );
                return;
        }

        std::vector<Utils::WString> files;
        if (remfile.containsAny(L"*?"))
        {
                for (size_t i = 0; i < cat.GetNumFiles(); i++)
                {
                        SInCatFile* f = cat.GetFile(i);
                        if (f && f->sFile.match(remfile))
                                files.push_back(f->sFile);
                }
        }
        else
                files.push_back(remfile);

        for (auto itr = files.begin(); itr != files.end(); itr++)
        {
                if (!cat.removeFile(*itr))
                        wprintf(L"Unable to find %s in cat file\n", itr->c_str());
                else
                        wprintf(L"%s: has been removed from archive\n", itr->c_str());
        }
}

void UnpackFile(const Utils::CommandLine &cmd)
{
        const Utils::WString& file = cmd.arg(1);
        const Utils::WString & tofile = cmd.arg(2);
        const Utils::WString& ext = cmd.switchData(L"ext");

        Utils::WStringList list;
        if(!findFiles(list, file, cmd) || !list.size())
        {
                wprintf(L"Error: no files found to unpack: %s\n", file.c_str());
                return;
        }

        for(auto itr = list.first(); itr; itr = list.next())
        {
                Utils::WString filename = itr->str;

                C_File f(filename);
                if (!f.CheckValidFilePointer())
                {
                        CFileIO File((file.contains(L":")) ? file : cmd.cmdDir() + L"/" + file);
                        filename = File.GetDirIO().file(itr->str);
                        f.setFilename(filename);
                }

                Utils::WString oldFilename = f.filename();

                if ( !f.CheckValidFilePointer() )
                        wprintf(L"Error: %s doesn't exists\n", filename.c_str() );
                else if ( !f.ReadFromFile() )
                        wprintf(L"Error: unable to open file: %s\n", filename.c_str() );
                else
                {
                        if(!f.UnPCKFile())
                                wprintf(L"Error: unable to unpack file: %s\n", filename.c_str());
                        else
                        {
                                if (tofile.empty())
                                        f.changeFileExt(ext.empty() ? L"xml" : ext);
                                else if (tofile.left(2) == L"*.")
                                        f.changeFileExt(CFileIO(tofile).extension().toString());
                                else
                                        f.setFilename(tofile);

                                if (!f.writeFilePointer())
                                        wprintf(L"Error: unable to write file: %s\n", tofile.c_str());
                                else
                                        wprintf(L"%s has been unpacked to %s\n", oldFilename.c_str(), f.filename().c_str());
                        }
                }
        }
}

void PackFile(const Utils::CommandLine &cmd)
{
        const Utils::WString& file = cmd.arg(1);
        const Utils::WString& tofile = cmd.arg(2);

        Utils::WStringList list;
        if(!findFiles(list, file, cmd) || !list.size())
        {
                wprintf(L"Error: no files found to pack: %s\n", file.c_str());
                return;
        }

        for(auto itr = list.first(); itr; itr = list.next())
        {
                Utils::WString filename = itr->str;

                C_File f(filename);
                if ( !f.CheckValidFilePointer() )
                        wprintf(L"Error: %s doesn't exists\n", filename.c_str() );
                else if ( !f.ReadFromFile() )
                        wprintf(L"Error: unable to open file: %s\n", filename.c_str() );
                else if ( !f.PCKFile() )
                        wprintf(L"Error: unable to pack file: %s\n", filename.c_str() );
                else
                {
                        if ( tofile.empty() )
                        {
                                if ( f.checkFileExt(L"bob") )
                                        f.changeFileExt(L"pbb");
                                else if ( f.checkFileExt(L"bod") )
                                        f.changeFileExt(L"pbd");
                                else
                                        f.changeFileExt(L"pck");
                        }
                        else if ( tofile.left(2) == L"*." )
                                f.changeFileExt(tofile.right(3));
                        else
                                f.setFilename(tofile);

                        if ( !f.writeFilePointer() )
                                wprintf(L"Error: unable to write file: %s\n", tofile.c_str() );
                        else
                                wprintf(L"%s has been packed to %s\n", CFileIO(filename).filename().c_str(), f.filename().c_str());
                }
        }
}

void BobFile(const Utils::CommandLine& cmd)
{
        Utils::WString filename = cmd.arg(1);
        Utils::WString tofile = cmd.arg(2);

        CFileIO File((filename.contains(L":")) ? filename : cmd.cmdDir() + L"/" + filename);

        C_File f(File.fullFilename());
        if (!f.CheckValidFilePointer())
                wprintf(L"Error: %s doesn't exists\n", filename.c_str());
        else if (!f.ReadFromFile())
                wprintf(L"Error: unable to open file: %s\n", filename.c_str());
        else
        {
                size_t size = 0;
                unsigned char* data = nullptr;

                if (f.checkFileExt(L"bob"))
                        data = f.BobDecompile(&size);
                else if (f.checkFileExt(L"bod"))
                        data = f.BobCompile(&size);
                else
                {
                        wprintf(L"Error: %s is not a valid BOB/BOD file\n", filename.c_str());
                        return;
                }

                if (data && size)
                {
                        if (!tofile.empty())
                                File.open((tofile.contains(L":")) ? tofile : cmd.cmdDir() + L"/" + tofile);
                        CFileIO ToFile(File.changeFileExtension(f.checkFileExt(L"bob") ? L"bod" : L"bob"));

                        if(!ToFile.WriteData((const char*)data, size))
                                wprintf(L"Error: unable to write file: %s\n", ToFile.filename().c_str());
                        else
                                wprintf(L"%s has been converted to %s\n", File.filename().c_str(), ToFile.filename().c_str());

                        delete[] data;
                }
                else
                {
                        if (f.checkFileExt(L"bob"))
                                wprintf(L"Error: unable to decompile file: %s\n", filename.c_str());
                        else
                                wprintf(L"Error: unable to compile file: %s\n", filename.c_str());
                }
        }
}

void PrintSyntax(const Utils::WString &cmd)
{
        wprintf(L"Syntax: %s <flags> [arguments]\n", cmd.c_str() );
        wprintf(L"Flags:\n");
        wprintf(L"\t-l <filename> [filemask]\n");
        wprintf(L"\t\tLists the contents of a cat file, with optional search mask\n");
        wprintf(L"\t-x [--preserve] <catfile::filemask> [dir]\n");
        wprintf(L"\t\textracts a file from a cat file to the optional directory\n\t--preserve: Preserves directory structure");
        wprintf(L"\t-xa <catfile> [dir]\n");
        wprintf(L"\t\textracts all files to optional directory\n");
        wprintf(L"\t-a <filename> <catfile::tofile>\n");
        wprintf(L"\t\tAdds the file into a cat archive, saves it as <tofile>\n");
        wprintf(L"\t-r <catfile> <file>\n");
        wprintf(L"\t\tRemoves a file from an archive\n");
        wprintf(L"\t[--ext:EXTENSION] -u <filename> [tofile]\n");
        wprintf(L"\t\tUnpacks a file, ie from pck to xml/txt\n");
        wprintf(L"\t-p <filename> <tofile>\n");
        wprintf(L"\t\tPacks a file, ie from xml/txt to pck\n");
        wprintf(L"\t-b <filename> <tofile>\n");
        wprintf(L"\t\tConverts between BOB and BOD files\n");
}

int main ( int argc, char **argv )
{
        printf ( "\nCATPCK Tool V1.22 17/05/2025 (SPK: %.2f) Created by Cycrow\n\n", GetLibraryVersion() );

        // parse the cmd name
        Utils::CommandLine cmd(argc, argv);

        if ( argc < 2 )
                PrintSyntax(cmd.cmdName());
        else
        {
                Utils::WString command(cmd.arg(0).lower());

                auto& args = cmd.args();

                if ( (command == L"-l") || (command == L"-list") )
                {
                        if (cmd.argCount() < 2)
                                wprintf(L"Syntax: %s -l <filename> [filemask]\n\tLists the contents of the cat file\n", cmd.cmdName().c_str());
                        else
                                ListFiles(cmd);
                }
                else if ((command == L"-x") || (command == L"-extract"))
                {
                        if (cmd.argCount() < 2 )
                                wprintf(L"Syntax: %s %s [--preserve] <catfile::filemask> [dir]\n\tExtracts a file from the cat archive\n", cmd.cmdName().c_str(), command.c_str() );
                        else
                                ExtractFile (cmd, L"", false);
                }
                else if ( (command == L"-xa") || (command == L"-extractall") )
                {
                        if (cmd.argCount() < 2)
                                wprintf(L"Syntax: %s %s <catfile> [dir]\n\tExtracts all files from the cat archive\n", cmd.cmdName().c_str(), command.c_str() );
                        else
                                ExtractFile(cmd, L"::*", true);
                }
                else if ( (command == L"-a") || (command == L"-append") )
                {
                        if (cmd.argCount() < 3 )
                                wprintf(L"Syntax: %s -a <filename> <catfile::tofile>\n\tAppends a file into the archive\n", cmd.cmdName().c_str() );
                        else
                                AppendFile(cmd);
                }
                else if ( (command == L"-r") || (command == L"-remove") )
                {
                        if (cmd.argCount() < 3 )
                                wprintf(L"Syntax: %s -r <catfile> <filename>\n\tRemoves a file from the archive\n", cmd.cmdName().c_str() );
                        else
                                RemoveFile(cmd);
                }
                else if ( (command == L"-u") || (command == L"-unpack") )
                {
                        if (cmd.argCount() < 2 )
                                wprintf(L"Syntax: %s [--ext:EXTENSION] -u <filename> [to]\n\tUnpacks a file", cmd.cmdName().c_str() );
                        else
                                UnpackFile(cmd);
                }
                else if ( (command == L"-p") || (command == L"-pack") )
                {
                        if (cmd.argCount() < 2 )
                                wprintf(L"Syntax: %s -p <filename> [to]\n\tPacks a file to .pck", cmd.cmdName().c_str() );
                        else
                                PackFile(cmd);
                }
                else if (command == L"-b")
                {
                        if (cmd.argCount() < 2)
                                wprintf(L"Syntax: %s -b <filename> [to]\n\tConverts a file between BOB and BOD", cmd.cmdName().c_str());
                        else
                                BobFile(cmd);
                }
                else
                {
                        wprintf(L"Invalid flag: %s\n\n", command.c_str());
                        PrintSyntax(cmd.cmdName());
                }
        }

#ifdef _DEBUG
        char pause;
        printf ( "\n\nPress a key to end\n" );
        scanf ( "%c", &pause );
#endif

        return 0;
}