summaryrefslogblamecommitdiff
path: root/src/fs/virtfs/virtfszip.cpp
blob: 425883f6d037a2596ea4ebea79ff4f238bf5bade (plain) (tree)



















                                                                         
                                


                     
                           
                        
                        




                                      











                                        
                      























                                                                               



























                                                                           




                       
                                                  

                                                   
                              







                                                                         

                                                                     


























                                                                         
                                            

                                             
                              







                                                                               

                                                                     




























                                                                         
                              























                                                                            
                              
































                                                                            















                                                   
                                                
     
                                





                                                              




                                                               



                                                                         


                             
                                 
     
                            





                                                              


                                                                         

     
                                                 

                                            
                               





                                                                      





                                                           


                                                                  
                           
         
                                                                               
             



                                                                       
                 

                                                            

















                                                                   































                                                                               





                                         
                               





                                                                   




                                                 















                                                                           
                                         
     
                            














                                                                        
                                            
     
                                





                                                                




                                                           





                                                                             
                                                                 




































































































































                                                                       
/*
 *  The ManaPlus Client
 *  Copyright (C) 2013-2017  The ManaPlus Developers
 *
 *  This file is part of The ManaPlus Client.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "fs/virtfs/virtfszip.h"

#include "fs/files.h"
#include "fs/paths.h"
#include "fs/virtfsfuncs.h"
#include "fs/virtfile.h"
#include "fs/virtlist.h"

#include "fs/virtfs/virtfileprivate.h"
#include "fs/virtfs/virtzipentry.h"
#include "fs/virtfs/zip.h"
#include "fs/virtfs/ziplocalheader.h"

#include "utils/checkutils.h"
#include "utils/dtor.h"
#include "utils/stringutils.h"

#include "debug.h"

extern const char *dirSeparator;

namespace
{
    std::vector<VirtZipEntry*> mEntries;
    VirtFsFuncs funcs;
}  // namespace

namespace VirtFsZip
{
    VirtZipEntry *searchEntryByArchive(const std::string &restrict archiveName)
    {
        FOR_EACH (std::vector<VirtZipEntry*>::const_iterator, it, mEntries)
        {
            if ((*it)->mArchiveName == archiveName)
                return *it;
        }
        return nullptr;
    }

    ZipLocalHeader *searchHeaderByName(const std::string &restrict filename)
    {
        FOR_EACH (std::vector<VirtZipEntry*>::const_iterator, it, mEntries)
        {
            VirtZipEntry *const entry = *it;
            FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
                      it2,
                      entry->mHeaders)
            {
                if ((*it2)->fileName == filename)
                    return *it2;
            }
        }
        return nullptr;
    }

    VirtZipEntry *searchZipEntryByNameWithDir(const std::string &restrict
                                              filename)
    {
        std::string dirName = filename;
        if (findLast(dirName, std::string(dirSeparator)) == false)
            dirName += dirSeparator;
        FOR_EACH (std::vector<VirtZipEntry*>::const_iterator, it, mEntries)
        {
            VirtZipEntry *const entry = *it;
            FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
                      it2,
                      entry->mHeaders)
            {
                if ((*it2)->fileName == filename)
                    return entry;
            }
            FOR_EACH (std::vector<std::string>::const_iterator,
                      it2,
                      entry->mDirs)
            {
                if (*it2 == dirName)
                    return entry;
            }
        }
        return nullptr;
    }

    bool addToSearchPathSilent(std::string newDir,
                               const Append append)
    {
        prepareFsPath(newDir);
        if (Files::existsLocal(newDir) == false)
        {
            logger->log("VirtFsZip::addToSearchPath file not exists: %s",
                newDir.c_str());
            return false;
        }
        if (findLast(newDir, ".zip") == false)
        {
            reportAlways("Called VirtFsZip::addToSearchPath without "
                "zip archive");
            return false;
        }
        VirtZipEntry *entry = VirtFsZip::searchEntryByArchive(newDir);
        if (entry != nullptr)
        {
            reportAlways("VirtFsZip::addToSearchPath already exists: %s",
                newDir.c_str());
            return false;
        }
        entry = new VirtZipEntry(newDir);
        if (Zip::readArchiveInfo(entry) == false)
        {
            delete entry;
            return false;
        }

        logger->log("Add virtual zip: " + newDir);
        if (append == Append_true)
            mEntries.push_back(entry);
        else
        {
            mEntries.insert(mEntries.begin(),
                entry);
        }
        return true;
    }

    bool addToSearchPath(std::string newDir,
                         const Append append)
    {
        prepareFsPath(newDir);
        if (Files::existsLocal(newDir) == false)
        {
            reportAlways("VirtFsZip::addToSearchPath directory not exists: %s",
                newDir.c_str());
            return false;
        }
        if (findLast(newDir, ".zip") == false)
        {
            reportAlways("Called VirtFsZip::addToSearchPath without "
                "zip archive");
            return false;
        }
        VirtZipEntry *entry = VirtFsZip::searchEntryByArchive(newDir);
        if (entry != nullptr)
        {
            reportAlways("VirtFsZip::addToSearchPath already exists: %s",
                newDir.c_str());
            return false;
        }
        entry = new VirtZipEntry(newDir);
        if (Zip::readArchiveInfo(entry) == false)
        {
            delete entry;
            return false;
        }

        logger->log("Add virtual zip: " + newDir);
        if (append == Append_true)
            mEntries.push_back(entry);
        else
        {
            mEntries.insert(mEntries.begin(),
                entry);
        }
        return true;
    }

    bool removeFromSearchPathSilent(std::string oldDir)
    {
        prepareFsPath(oldDir);
        if (findLast(oldDir, ".zip") == false)
        {
            reportAlways("Called removeFromSearchPath without zip archive");
            return false;
        }
        FOR_EACH (std::vector<VirtZipEntry*>::iterator, it, mEntries)
        {
            VirtZipEntry *const entry = *it;
            if (entry->mArchiveName == oldDir)
            {
                logger->log("Remove virtual zip: " + oldDir);
                mEntries.erase(it);
                delete entry;
                return true;
            }
        }

        logger->log("VirtFsZip::removeFromSearchPath not exists: %s",
            oldDir.c_str());
        return false;
    }

    bool removeFromSearchPath(std::string oldDir)
    {
        prepareFsPath(oldDir);
        if (findLast(oldDir, ".zip") == false)
        {
            reportAlways("Called removeFromSearchPath without zip archive");
            return false;
        }
        FOR_EACH (std::vector<VirtZipEntry*>::iterator, it, mEntries)
        {
            VirtZipEntry *const entry = *it;
            if (entry->mArchiveName == oldDir)
            {
                logger->log("Remove virtual zip: " + oldDir);
                mEntries.erase(it);
                delete entry;
                return true;
            }
        }

        reportAlways("VirtFsZip::removeFromSearchPath not exists: %s",
            oldDir.c_str());
        return false;
    }

    std::vector<VirtZipEntry*> &getEntries()
    {
        return mEntries;
    }

    void deinit()
    {
        delete_all(mEntries);
        mEntries.clear();
    }

    void init()
    {
        initFuncs(&funcs);
    }

    void initFuncs(VirtFsFuncs *restrict const ptr)
    {
        ptr->close = &VirtFsZip::close;
        ptr->read = &VirtFsZip::read;
        ptr->write = &VirtFsZip::write;
        ptr->fileLength = &VirtFsZip::fileLength;
        ptr->tell = &VirtFsZip::tell;
        ptr->seek = &VirtFsZip::seek;
        ptr->eof = &VirtFsZip::eof;
    }

    std::string getRealDir(std::string filename)
    {
        prepareFsPath(filename);
        if (checkPath(filename) == false)
        {
            reportAlways("VirtFsZip::exists invalid path: %s",
                filename.c_str());
            return std::string();
        }
        return getRealDirInternal(filename);
    }

    std::string getRealDirInternal(const std::string &filename)
    {
        VirtZipEntry *restrict const entry = searchZipEntryByNameWithDir(
            filename);
        if (entry != nullptr)
            return entry->mArchiveName;
        return std::string();
    }

    bool exists(std::string name)
    {
        prepareFsPath(name);
        if (checkPath(name) == false)
        {
            reportAlways("VirtFsZip::exists invalid path: %s",
                name.c_str());
            return false;
        }
        VirtZipEntry *restrict const entry = searchZipEntryByNameWithDir(
            name);
        return entry != nullptr;
    }

    VirtList *enumerateFiles(std::string dirName)
    {
        VirtList *const list = new VirtList;
        prepareFsPath(dirName);
        if (checkPath(dirName) == false)
        {
            reportAlways("VirtFsZip::enumerateFiles invalid path: %s",
                dirName.c_str());
            return list;
        }
        return enumerateFiles(dirName, list);
    }

    VirtList *enumerateFiles(std::string dirName,
                             VirtList *restrict const list)
    {
        if (findLast(dirName, std::string(dirSeparator)) == false)
            dirName += dirSeparator;
        StringVect &names = list->names;
        if (dirName == "/")
        {
            FOR_EACH (std::vector<VirtZipEntry*>::const_iterator, it, mEntries)
            {
                VirtZipEntry *const entry = *it;
                FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
                          it2,
                          entry->mHeaders)
                {
                    ZipLocalHeader *const header = *it2;
                    std::string fileName = header->fileName;
                    // skip subdirs from enumeration
                    const size_t idx = fileName.find(dirSeparator);
                    if (idx != std::string::npos)
                        fileName.erase(idx);
                    bool found(false);
                    FOR_EACH (StringVectCIter, itn, names)
                    {
                        if (*itn == fileName)
                        {
                            found = true;
                            break;
                        }
                    }
                    if (found == false)
                        names.push_back(fileName);
                }
            }
        }
        else
        {
            FOR_EACH (std::vector<VirtZipEntry*>::const_iterator, it, mEntries)
            {
                VirtZipEntry *const entry = *it;
                FOR_EACH (std::vector<ZipLocalHeader*>::const_iterator,
                          it2,
                          entry->mHeaders)
                {
                    ZipLocalHeader *const header = *it2;
                    std::string fileName = header->fileName;
                    if (findCutFirst(fileName, dirName) == true)
                    {
                        // skip subdirs from enumeration
                        const size_t idx = fileName.find(dirSeparator);
                        if (idx != std::string::npos)
                            fileName.erase(idx);
                        bool found(false);
                        FOR_EACH (StringVectCIter, itn, names)
                        {
                            if (*itn == fileName)
                            {
                                found = true;
                                break;
                            }
                        }
                        if (found == false)
                            names.push_back(fileName);
                    }
                }
            }
        }

        return list;
    }

    bool isDirectory(std::string dirName)
    {
        prepareFsPath(dirName);
        if (checkPath(dirName) == false)
        {
            reportAlways("VirtFsZip::isDirectory invalid path: %s",
                dirName.c_str());
            return false;
        }
        return isDirectoryInternal(dirName);
    }

    bool isDirectoryInternal(std::string dirName)
    {
        if (findLast(dirName, std::string(dirSeparator)) == false)
            dirName += dirSeparator;
        FOR_EACH (std::vector<VirtZipEntry*>::const_iterator, it, mEntries)
        {
            VirtZipEntry *const entry = *it;
            FOR_EACH (std::vector<std::string>::const_iterator,
                      it2,
                      entry->mDirs)
            {
                if (*it2 == dirName)
                    return true;
            }
        }
        return false;
    }

    bool isSymbolicLink(std::string name)
    {
        prepareFsPath(name);
        if (checkPath(name) == false)
        {
            reportAlways("VirtFsZip::isSymbolicLink invalid path: %s",
                name.c_str());
            return false;
        }
        // look like in zip files can be symlinks, but here they useless
        return false;
    }

    void freeList(VirtList *restrict const handle)
    {
        delete handle;
    }

    VirtFile *openRead(std::string filename)
    {
        prepareFsPath(filename);
        if (checkPath(filename) == false)
        {
            reportAlways("VirtFsZip::openRead invalid path: %s",
                filename.c_str());
            return nullptr;
        }
        return openReadInternal(filename);
    }

    VirtFile *openReadInternal(const std::string &filename)
    {
        ZipLocalHeader *restrict const header = searchHeaderByName(filename);
        if (header != nullptr)
        {
            uint8_t *restrict const buf = Zip::readFile(header);
            if (buf == nullptr)
                return nullptr;
            VirtFile *restrict const file = new VirtFile(&funcs);
            file->mPrivate = new VirtFilePrivate(buf,
                header->uncompressSize);
            return file;
        }
        return nullptr;
    }

    VirtFile *openWrite(const std::string &restrict filename A_UNUSED)
    {
        return nullptr;
    }

    VirtFile *openAppend(const std::string &restrict filename A_UNUSED)
    {
        return nullptr;
    }

    bool setWriteDir(const std::string &restrict newDir A_UNUSED)
    {
        return false;
    }

    bool mkdir(const std::string &restrict dirname A_UNUSED)
    {
        return false;
    }

    bool remove(const std::string &restrict filename A_UNUSED)
    {
        return false;
    }

    void permitLinks(const bool val A_UNUSED)
    {
    }

    const char *getLastError()
    {
        return nullptr;
    }

    int close(VirtFile *restrict const file)
    {
        if (file == nullptr)
            return 0;
        delete file;
        return 1;
    }

    int64_t read(VirtFile *restrict const file,
                 void *restrict const buffer,
                 const uint32_t objSize,
                 const uint32_t objCount)
    {
        if (file == nullptr ||
            objSize == 0 ||
            objCount == 0)
        {
            return 0;
        }
        if (buffer == nullptr)
        {
            reportAlways("VirtFsZip::read buffer is null");
            return 0;
        }
        VirtFilePrivate *restrict const priv = file->mPrivate;
        const uint32_t pos = priv->mPos;
        const uint32_t sz = priv->mSize;
        // if outside of buffer, return
        if (pos >= sz)
            return 0;
        // pointer to start for buffer ready to read
        const uint8_t *restrict const memPtr = priv->mBuf + pos;
        // left buffer size from pos to end
        const uint32_t memSize = sz - pos;
        // number of objects possible to read
        uint32_t memCount = memSize / objSize;
        if (memCount == 0)
            return 0;
        // limit number of possible objects to read to objCount
        if (memCount > objCount)
            memCount = objCount;
        // number of bytes to read from buffer
        const uint32_t memEnd = memCount * objSize;
        memcpy(buffer, memPtr, memEnd);
        priv->mPos += memEnd;
        return memCount;
    }

    int64_t write(VirtFile *restrict const file A_UNUSED,
                  const void *restrict const buffer A_UNUSED,
                  const uint32_t objSize A_UNUSED,
                  const uint32_t objCount A_UNUSED)
    {
        return 0;
    }

    int64_t fileLength(VirtFile *restrict const file)
    {
        if (file == nullptr)
            return -1;

        return file->mPrivate->mSize;
    }

    int64_t tell(VirtFile *restrict const file)
    {
        if (file == nullptr)
            return -1;

        return file->mPrivate->mPos;
    }

    int seek(VirtFile *restrict const file,
             const uint64_t pos)
    {
        if (file == nullptr)
            return 0;

        if (pos > file->mPrivate->mSize)
            return 0;
        file->mPrivate->mPos = pos;
        return 1;
    }

    int eof(VirtFile *restrict const file)
    {
        if (file == nullptr)
            return -1;

        return file->mPrivate->mPos >= file->mPrivate->mSize;
    }
}  // namespace VirtFsZip