1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#if defined(ZIP_SUPPORT)
19
20#ifndef FS_NODE_ZIP_HXX
21#define FS_NODE_ZIP_HXX
22
23#include "ZipHandler.hxx"
24#include "FSNode.hxx"
25
26/*
27 * Implementation of the Stella file system API based on ZIP archives.
28 * ZIP archives are treated as directories if the contain more than one ROM
29 * file, as a file if they contain a single ROM file, and as neither if the
30 * archive is empty. Hence, if a ZIP archive isn't a directory *or* a file,
31 * it is invalid.
32 *
33 * Parts of this class are documented in the base interface class, AbstractFSNode.
34 */
35class FilesystemNodeZIP : public AbstractFSNode
36{
37 public:
38 /**
39 * Creates a FilesystemNodeZIP with the root node as path.
40 */
41 FilesystemNodeZIP();
42
43 /**
44 * Creates a FilesystemNodeZIP for a given path.
45 *
46 * @param path String with the path the new node should point to.
47 */
48 explicit FilesystemNodeZIP(const string& path);
49
50 bool exists() const override { return _realNode && _realNode->exists(); }
51 const string& getName() const override { return _name; }
52 void setName(const string& name) override { _name = name; }
53 const string& getPath() const override { return _path; }
54 string getShortPath() const override { return _shortPath; }
55 bool hasParent() const override { return true; }
56 bool isDirectory() const override { return _isDirectory; }
57 bool isFile() const override { return _isFile; }
58 bool isReadable() const override { return _realNode && _realNode->isReadable(); }
59 bool isWritable() const override { return false; }
60
61 //////////////////////////////////////////////////////////
62 // For now, ZIP files cannot be modified in any way
63 bool makeDir() override { return false; }
64 bool rename(const string& newfile) override { return false; }
65 //////////////////////////////////////////////////////////
66
67 bool getChildren(AbstractFSList& list, ListMode mode) const override;
68 AbstractFSNodePtr getParent() const override;
69
70 uInt32 read(ByteBuffer& image) const override;
71
72 private:
73 FilesystemNodeZIP(const string& zipfile, const string& virtualpath,
74 AbstractFSNodePtr realnode, bool isdir);
75
76 void setFlags(const string& zipfile, const string& virtualpath,
77 AbstractFSNodePtr realnode);
78
79 friend ostream& operator<<(ostream& os, const FilesystemNodeZIP& node)
80 {
81 os << "_zipFile: " << node._zipFile << endl
82 << "_virtualPath: " << node._virtualPath << endl
83 << "_path: " << node._path << endl
84 << "_shortPath: " << node._shortPath << endl;
85 return os;
86 }
87
88 private:
89 /* Error types */
90 enum class zip_error
91 {
92 NONE,
93 NOT_A_FILE,
94 NOT_READABLE,
95 NO_ROMS
96 };
97
98 // Since a ZIP file is itself an abstraction, it still needs access to
99 // an actual concrete filesystem node
100 AbstractFSNodePtr _realNode;
101
102 string _zipFile, _virtualPath;
103 string _name, _path, _shortPath;
104 zip_error _error;
105 uInt32 _numFiles;
106
107 bool _isDirectory, _isFile;
108
109 // ZipHandler static reference variable responsible for accessing ZIP files
110 static unique_ptr<ZipHandler> myZipHandler;
111
112 // Get last component of path
113 static const char* lastPathComponent(const string& str)
114 {
115 if(str.empty())
116 return "";
117
118 const char* start = str.c_str();
119 const char* cur = start + str.size() - 2;
120
121 while (cur >= start && !(*cur == '/' || *cur == '\\'))
122 --cur;
123
124 return cur + 1;
125 }
126};
127
128#endif
129
130#endif // ZIP_SUPPORT
131