Commits

Mathias Panzenböck committed ad1c6ec

fixed entries embedded in the directory file

Comments (0)

Files changed (10)

 	      ?      1  U16     CRC32 checksum
 	+0x0002      1  U16     Inlined file size (IFS).
 	+0x0004      1  U16     Archive index. This is the "###" part of the
-	                        archive file names.
+	                        archive file names. If the index is 0x7fff (max signed
+	                        16bit int) then the file is embedded in the directory
+	                        file instead of a separate archive.
 	+0x0006      1  U32     Offset within the archive where the file starts.
+	                        If the file is embedded in the direcory file (index =
+	                        0x7fff) this is relative to the end of the file index
+	                        (i.e. (0 or 12) + index_size).
 	+0x000A      1  U32     File size. A file size of 0 indicates that the file
 	                        data is inlined in the directory file.
 	+0x000E      1  U16     Terminator: 0xFFFF

libvpk/include/vpk/dir.h

 #define VPK_DIR_H
 
 #include <string>
+#include <vector>
 
 #include <vpk/node.h>
 #include <vpk/file_io.h>
 
 namespace Vpk {
+	class File;
+
 	class Dir : public Node {
 	public:
 		typedef Nodes::iterator iterator;
 		Dir(const std::string &name) : Node(name), m_subdirs(0) {}
 
 		Type type() const { return Node::DIR; }
-		void read(FileIO &io, const std::string &path, const std::string &type);
+		void read(FileIO &io, const std::string &path, const std::string &type, std::vector<File*> &dirfiles);
 
 		const Nodes &nodes() const { return m_nodes; }
 		const Node *node(const std::string &name) const;

libvpk/include/vpk/file.h

 			preload(0, 0) {}
 
 		Type type() const { return Node::FILE; }
-		void read(FileIO &io);
+		void read(FileIO &io, std::vector<File*> &dirfiles);
 
 		uint32_t crc32;
 		uint32_t size;

libvpk/include/vpk/package.h

 	class Package : public Dir {
 	public:
 		Package(Handler *handler = 0) :
-			Dir(""), m_srcdir("."), m_handler(handler) {}
+			Dir(""), m_version(0), m_dataoff(0), m_srcdir("."), m_handler(handler) {}
 
 		void read(const char *path) { read(boost::filesystem::path(path)); }
 		void read(const std::string &path) { read(boost::filesystem::path(path)); }
 		void read(const char *path, FileIO &io) { read(boost::filesystem::path(path), io); }
 		void read(const std::string &path, FileIO &io) { read(boost::filesystem::path(path), io); }
 		void read(const boost::filesystem::path &path, FileIO &io);
-		void read(const std::string &srcdir, const std::string &name, FileIO &io);
 
 		Dir &mkpath(const std::string &path) { return mkpath(path.c_str()); }
 		Dir &mkpath(const char *path);
 
 		std::string             archiveName(uint16_t index) const;
 		boost::filesystem::path archivePath(uint16_t index) const;
+		unsigned int version() const { return m_version; }
+		unsigned int dataoff() const { return m_dataoff; }
 		const std::string &srcdir() const { return m_srcdir; }
+		const std::string &dirfile() const { return m_dirfile; }
 		Node *get(const std::string &path) { return get(path.c_str()); }
 		Node *get(const char *path);
 		void setHandler(Handler *handler) { m_handler = handler; }
 		bool error(const std::string &msg, const std::string &path, ErrorMethod handler) const;
 		bool error(const std::exception &exc, const std::string &path, ErrorMethod handler) const;
 
-		std::string m_srcdir;
-		Handler    *m_handler;
+		unsigned int m_version;
+		unsigned int m_dataoff;
+		std::string  m_srcdir;
+		std::string  m_dirfile;
+		Handler     *m_handler;
 	};
 }
 

libvpk/src/dir.cpp

 #include <vpk/dir.h>
 #include <vpk/file.h>
 
-void Vpk::Dir::read(FileIO &io, const std::string &path, const std::string &type) {
+void Vpk::Dir::read(FileIO &io, const std::string &path, const std::string &type, std::vector<File*> &dirfiles) {
 	// files
 	for (;;) {
 		std::string name;
 		}
 		File *file = new File(name);
 		m_nodes[name] = NodePtr(file);
-		file->read(io);
+		file->read(io, dirfiles);
 	}
 }
 

libvpk/src/file.cpp

 #include <vpk/file.h>
 #include <vpk/file_format_error.h>
 
-void Vpk::File::read(FileIO &io) {
+void Vpk::File::read(FileIO &io, std::vector<File*> &dirfiles) {
 	crc32 = io.readLU32();
 	unsigned int length = io.readLU16();
 	index = io.readLU16();
 		preload.resize(length, 0);
 		io.read(&preload[0], length);
 	}
+
+	if (index == 0x7fff) {
+		dirfiles.push_back(this);
+	}
 }

libvpk/src/package.cpp

 namespace fs   = boost::filesystem;
 namespace algo = boost::algorithm;
 
-void Vpk::Package::read(const boost::filesystem::path &path) {
+void Vpk::Package::read(const fs::path &path) {
 	FileIO io(path, "rb");
 	read(path, io);
 }
 
-void Vpk::Package::read(const boost::filesystem::path &path, FileIO &io) {
-	std::string filename(path.filename());
+void Vpk::Package::read(const fs::path &path, FileIO &io) {
+	fs::path abspath = fs::system_complete(path);
+	m_dirfile = abspath.filename();
 		
-	if (filename.size() < 8 || tolower(filename.substr(filename.size()-8)) != "_dir.vpk") {
+	if (m_dirfile.size() < 8 || tolower(m_dirfile.substr(m_dirfile.size()-8)) != "_dir.vpk") {
 		Exception exc((boost::format("file does not end in \"_dir.vpk\": \"%s\"")
-			% path.string()).str());
-		if (m_handler) {
-			if (m_handler->archiveerror(exc, path.string())) {
-				throw exc;
-			}
-			setName(filename);
-		}
-		else {
+			% abspath.string()).str());
+		if (archiveerror(exc, abspath.string())) {
 			throw exc;
 		}
+		setName(m_dirfile);
 	}
 	else {
-		setName(filename.substr(0, filename.size()-8));
+		setName(m_dirfile.substr(0, m_dirfile.size()-8));
 	}
-	m_srcdir = fs::system_complete(path).parent_path().string();
+	m_srcdir = abspath.parent_path().string();
 	
 	read(io);
 }
 
-void Vpk::Package::read(const std::string &srcdir, const std::string &name, FileIO &io) {
-	m_srcdir = fs::system_complete(srcdir).string();
-	setName(name);
-	read(io);
-}
-
 void Vpk::Package::read(FileIO &io) {
+	size_t headerSize = 0;
+	unsigned int indexSize = 0;
 	if (io.readLU32() != 0x55AA1234) {
 		io.seek(-4, FileIO::CUR);
 	}
 	else {
-		unsigned int version      =    io.readLU32();
-		/* unsigned int indexSize = */ io.readLU32();
+		m_version  = io.readLU32();
+		indexSize  = io.readLU32();
+		headerSize = io.tell();
+		m_dataoff  = indexSize + headerSize;
 
-		if (version != 1) {
+		if (m_version != 1) {
 			throw FileFormatError((boost::format("unsupported VPK version: %u")
-				% version).str());
+				% m_version).str());
 		}
 	}
 
+	std::vector<File*> dirfiles;
+
 	// types
 	for (;;) {
 		std::string type;
 			io.readAsciiZ(path);
 			if (path.empty()) break;
 
-			mkpath(path).read(io, path, type);
+			mkpath(path).read(io, path, type, dirfiles);
 		}
 	}
+
+	if (m_version == 0) {
+		m_dataoff = io.tell();
+	}
+	else if (io.tell() != m_dataoff) {
+		Exception exc(
+			(boost::format("missmatch between header index size (%u) and real index size (%u)")
+			% indexSize % (io.tell() - headerSize)).str());
+		if (archiveerror(exc, (fs::path(m_srcdir) / (name() + "_dir.vpk")).string())) {
+			throw exc;
+		}
+	}
+
+	for (std::vector<File*>::iterator i = dirfiles.begin(); i != dirfiles.end(); ++ i) {
+		(*i)->offset += m_dataoff;
+	}
 }
 
 Vpk::Dir &Vpk::Package::mkpath(const char *path) {
 }
 
 std::string Vpk::Package::archiveName(uint16_t index) const {
-	return (boost::format("%s_%03d.vpk") % name() % index).str();
+	if (index == 0x7fff) {
+		return m_dirfile;
+	}
+	else {
+		return (boost::format("%s_%03d.vpk") % name() % index).str();
+	}
 }
 
 boost::filesystem::path Vpk::Package::archivePath(uint16_t index) const {

unvpk/src/main.cpp

 }
 
 static void coverage(
-		const fs::path &archindex,
-		off_t dirPos,
 		const Package &package,
 		bool dump,
 		const fs::path &destdir,
 		bool humanreadable) {
 	Coverages covs;
 	
-	covs[-1].add(0, dirPos);
+	covs[0x7fff].add(0, package.dataoff());
 
 	std::string prefix = tolower(package.name());
 	prefix += '_';
 	const size_t magicSize = Magic::maxSize();
 	std::vector<char> magic(magicSize, 0);
 	for (Coverages::const_iterator i = covs.begin(); i != covs.end(); ++ i) {
-		std::string archive;
-		size_t size;
-
-		if (i->first == -1) {
-			size = fs::file_size(archindex);
-			archive = archindex.filename();
-		}
-		else {
-			fs::path path = package.archivePath(i->first);
-			size = fs::file_size(path);
-			archive = path.filename();
-		}
+		fs::path path = package.archivePath(i->first);
+		std::string archive = path.filename();
+		size_t size = fs::file_size(path);
 
 		total += size;
 		std::string sizeStr = humanreadable ?
 	Package package(&handler);
 
 	try {
-		FileIO io(archive);
-		package.read(archive, io);
-		off_t pos = io.tell();
-		io.close();
+		package.read(archive);
 
 		if (!filter.empty()) {
 			package.filter(filter);
 		}
 
 		if (coverage || dump) {
-			::coverage(archive, pos, package, dump, directory, humanreadable);
+			::coverage(package, dump, directory, humanreadable);
 		}
 		else if (list) {
 			::list(package, humanreadable, sorting);

vpkfs/include/vpk/vpkfs.h

 #include <vector>
 
 #include <boost/unordered_set.hpp>
+#include <boost/unordered_map.hpp>
 
 #include <fuse.h>
 
 	private:
 		void statfs(const Node *node);
 
-		typedef std::vector<int> Archives;
+		typedef boost::unordered_map<uint16_t,int> Archives;
 		typedef boost::unordered_set<uint16_t> Indices;
 
 		FuseArgs         m_args;

vpkfs/src/vpkfs.cpp

 
 	m_files = 0;
 	statfs(&m_package);
-	int archspace = 0;
-	for (Indices::const_iterator i = m_indices.begin(); i != m_indices.end(); ++ i) {
-		int needspace = *i + 1;
-		if (needspace > archspace) {
-			archspace = needspace;
-		}
-	}
-	
-	m_archives.resize(archspace, -1);
+
 	for (Indices::const_iterator i = m_indices.begin(); i != m_indices.end(); ++ i) {
 		uint16_t index = *i;
 		fs::path archivePath(m_package.archivePath(index));
 }
 
 void Vpk::Vpkfs::clear() {
-	for (size_t i = 0, n = m_archives.size(); i < n; ++ i) {
-		int fd = m_archives[i];
+	for (Archives::iterator i = m_archives.begin(); i != m_archives.end(); ++ i) {
+		int fd = i->second;
 		if (fd >= 0 && ::close(fd) != 0) {
 			int errnum = errno;
 			std::cerr
 				<< "*** error closing archive \""
-				<< m_package.archivePath(i) << "\": "
+				<< m_package.archivePath(i->first) << "\": "
 				<< strerror(errnum) << std::endl;
 		}
-		m_archives[i] = -1;
+		m_archives[i->first] = -1;
 	}
 	m_archives.clear();
 	m_indices.clear();