mirror of
https://github.com/stevenhowes/PakFS.git
synced 2026-05-27 00:03:39 +01:00
First version
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
TARGET1 = example1
|
||||||
|
TARGET2 = example2
|
||||||
|
|
||||||
|
SRCS_TARGET1 = example1.cpp $(wildcard PakFS/*.cpp)
|
||||||
|
SRCS_TARGET2 = example2.cpp $(wildcard PakFS/*.cpp)
|
||||||
|
|
||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -Wall -Wextra
|
||||||
|
|
||||||
|
OBJ_DIR := ./objects
|
||||||
|
OBJS_TARGET1 := $(SRCS_TARGET1:%.cpp=$(OBJ_DIR)/%.o)
|
||||||
|
OBJS_TARGET2 := $(SRCS_TARGET2:%.cpp=$(OBJ_DIR)/%.o)
|
||||||
|
|
||||||
|
all: build $(TARGET1) $(TARGET2)
|
||||||
|
|
||||||
|
build:
|
||||||
|
@mkdir -p ./$(OBJ_DIR)/PakFS
|
||||||
|
valgrind: all
|
||||||
|
valgrind ./$(TARGET1)
|
||||||
|
valgrind ./$(TARGET2)
|
||||||
|
|
||||||
|
$(TARGET1): $(OBJS_TARGET1)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $(TARGET1) $(OBJS_TARGET1)
|
||||||
|
|
||||||
|
$(TARGET2): $(OBJS_TARGET2)
|
||||||
|
$(CXX) $(CXXFLAGS) -o $(TARGET2) $(OBJS_TARGET2)
|
||||||
|
|
||||||
|
$(OBJ_DIR)/%.o: %.cpp
|
||||||
|
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET1) $(TARGET2)
|
||||||
|
rm -rf $(OBJ_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#include "PakFS.hpp"
|
||||||
|
#include <iomanip>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
PakFS::PakFS(const std::string& baseDir, bool paksOnly) {
|
||||||
|
base = baseDir;
|
||||||
|
|
||||||
|
for(int i = 0; i < MAX_PAKS; i++) {
|
||||||
|
std::string path = baseDir + "/pak" + std::to_string(i) + ".pak";
|
||||||
|
|
||||||
|
if(!std::ifstream (path)) {
|
||||||
|
pakFiles[i] = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pakFiles[i] = new PakFile(path);
|
||||||
|
|
||||||
|
for (const auto& file : pakFiles[i]->getFileNames()) {
|
||||||
|
files[file] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!paksOnly) {
|
||||||
|
ParseDirectory("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PakFS::PakFS(const std::string& baseDir) : PakFS(baseDir, true) {}
|
||||||
|
|
||||||
|
PakFS::~PakFS() {
|
||||||
|
for (int i = 0; i < MAX_PAKS; i++) {
|
||||||
|
if (pakFiles[i]) {
|
||||||
|
delete pakFiles[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PakFS::getFileSize(const std::string& t_filename) {
|
||||||
|
if(files[t_filename] == -1) {
|
||||||
|
std::ifstream file(base + "/" + t_filename, std::ios::binary | std::ios::ate);
|
||||||
|
return file.tellg();
|
||||||
|
}else{
|
||||||
|
return pakFiles[files[t_filename]]->getFileSize(t_filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PakFS::getFile(const std::string& t_filename, uint8_t* buffer) {
|
||||||
|
if(files[t_filename] == -1) {
|
||||||
|
std::string filename = base + "/" + t_filename;
|
||||||
|
std::ifstream file(filename, std::ios::binary);
|
||||||
|
file.read(reinterpret_cast<char*>(buffer), getFileSize(t_filename));
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
pakFiles[files[t_filename]]->getFile(t_filename, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PakFS::getFileString(const std::string& t_filename) {
|
||||||
|
int size = getFileSize(t_filename);
|
||||||
|
uint8_t* ByteArray;
|
||||||
|
ByteArray = new uint8_t[size + 1];
|
||||||
|
getFile(t_filename, ByteArray);
|
||||||
|
ByteArray[size] = 0;
|
||||||
|
std::string str(reinterpret_cast<char*>(ByteArray));
|
||||||
|
delete [] ByteArray;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PakFS::listFiles() {
|
||||||
|
std::cout << "--------------" << std::endl << "PAK | Filename" << std::endl << "--------------" << std::endl;
|
||||||
|
for (const auto& file : files) {
|
||||||
|
std::cout << std::setw(3) << std::setfill(' ') << file.second << " | " << file.first << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PakFS::ParseDirectory(const std::string& t_path) {
|
||||||
|
std::string path = t_path;
|
||||||
|
|
||||||
|
if(!path.empty()) {
|
||||||
|
path += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(base + "/" + t_path)) {
|
||||||
|
if (entry.is_regular_file()) {
|
||||||
|
if (entry.path().extension().string() == ".pak") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
files[path + entry.path().filename().string()] = -1;
|
||||||
|
}else if(entry.is_directory()){
|
||||||
|
ParseDirectory(entry.path().string().substr(base.size() + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef PAKFS_HPP
|
||||||
|
#define PAKFS_HPP
|
||||||
|
|
||||||
|
#include "PakFile.hpp"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#define MAX_PAKS 10
|
||||||
|
|
||||||
|
class PakFS {
|
||||||
|
public:
|
||||||
|
PakFS(const std::string& baseDir, bool paksOnly);
|
||||||
|
PakFS(const std::string& baseDir);
|
||||||
|
~PakFS();
|
||||||
|
|
||||||
|
int getFileSize(const std::string& filename);
|
||||||
|
void getFile(const std::string& filename, uint8_t* buffer);
|
||||||
|
std::string getFileString(const std::string& filename);
|
||||||
|
void listFiles();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, int> files;
|
||||||
|
PakFile* pakFiles[MAX_PAKS];
|
||||||
|
std::string base {""};
|
||||||
|
|
||||||
|
void ParseDirectory(const std::string& path);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PAKFS_HPP
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "PakFile.hpp"
|
||||||
|
|
||||||
|
PakFile::PakFile(const std::string& filename) {
|
||||||
|
file.open(filename, std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
throw std::runtime_error("Failed to open PAK file");
|
||||||
|
}
|
||||||
|
readHeader();
|
||||||
|
readDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
PakFile::~PakFile() {
|
||||||
|
if (file.is_open()) {
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> PakFile::getFileNames() {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
for (const auto& entry : entries) {
|
||||||
|
names.push_back(entry.name);
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PakFile::getFileSize(const std::string& filename) {
|
||||||
|
for (const auto& entry : entries) {
|
||||||
|
if (filename == entry.name) {
|
||||||
|
return entry.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PakFile::getFile(const std::string& filename, uint8_t* buffer) {
|
||||||
|
for (const auto& entry : entries) {
|
||||||
|
if (filename == entry.name) {
|
||||||
|
file.seekg(entry.offset, std::ios::beg);
|
||||||
|
file.read(reinterpret_cast<char*>(buffer), entry.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PakFile::readHeader() {
|
||||||
|
file.read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||||
|
if (std::string(header.id, 4) != "PACK") {
|
||||||
|
throw std::runtime_error("Invalid PAK file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PakFile::readDirectory() {
|
||||||
|
file.seekg(header.dirOffset, std::ios::beg);
|
||||||
|
int numEntries = header.dirLength / sizeof(PakEntry);
|
||||||
|
entries.resize(numEntries);
|
||||||
|
file.read(reinterpret_cast<char*>(entries.data()), header.dirLength);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef PAKFILE_HPP
|
||||||
|
#define PAKFILE_HPP
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class PakFile {
|
||||||
|
public:
|
||||||
|
PakFile(const std::string& filename);
|
||||||
|
~PakFile();
|
||||||
|
|
||||||
|
std::vector<std::string> getFileNames();
|
||||||
|
|
||||||
|
int getFileSize(const std::string& filename);
|
||||||
|
void getFile(const std::string& filename, uint8_t* buffer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct PakHeader {
|
||||||
|
char id[4];
|
||||||
|
uint32_t dirOffset;
|
||||||
|
uint32_t dirLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PakEntry {
|
||||||
|
char name[56];
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ifstream file;
|
||||||
|
PakHeader header;
|
||||||
|
std::vector<PakEntry> entries;
|
||||||
|
|
||||||
|
void readHeader();
|
||||||
|
void readDirectory();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PAKFILE_HPP
|
||||||
@@ -1 +1,11 @@
|
|||||||
# private-libpakfs
|
# PakFS
|
||||||
|
|
||||||
|
A simple C++ class for handling Quake2 PAK files from a base folder (and optionally including non-PAKed files). Two examples are included, along with an example base folder with bare files, a base PAK (pak0) and a PAK file with some overrides (pak2). The file pak1 intentionally doesn't exist.
|
||||||
|
|
||||||
|
## example1.cpp
|
||||||
|
|
||||||
|
Reads a single text file as a string from PAK files only.
|
||||||
|
|
||||||
|
## example2.cpp
|
||||||
|
|
||||||
|
Reads a selection of files from PAK files and the base directory, storing them in a char array (as you would for a binary file).
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
this only exists within subfolders under base
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
this file only exists in the base folder
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
This is a test file that's not in a pak
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "PakFS/PakFS.hpp"
|
||||||
|
|
||||||
|
#define PAKFS_BASE "base"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
PakFS pakfs(PAKFS_BASE);
|
||||||
|
|
||||||
|
std::string filename = "testfile";
|
||||||
|
|
||||||
|
int filesize = pakfs.getFileSize(filename);
|
||||||
|
|
||||||
|
if(filesize >= 0)
|
||||||
|
{
|
||||||
|
std::cout << "Size: " << filesize << std::endl << "----" << std::endl;
|
||||||
|
std::cout << pakfs.getFileString(filename);
|
||||||
|
}else{
|
||||||
|
std::cout << "File not found: " << filename << std::endl << std::endl;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "PakFS/PakFS.hpp"
|
||||||
|
|
||||||
|
#define PAKFS_BASE "base"
|
||||||
|
#define PAKFS_PAKS_ONLY false
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
PakFS pakfs(PAKFS_BASE, PAKFS_PAKS_ONLY);
|
||||||
|
|
||||||
|
pakfs.listFiles();
|
||||||
|
|
||||||
|
std::vector<std::string> filenames{ "testfile", "testfile2", "folder/testfile3" , "folder2/testfile4", "folder2/subfolder1/testfile5" };
|
||||||
|
|
||||||
|
for (const auto& filename : filenames) {
|
||||||
|
int filesize = pakfs.getFileSize(filename);
|
||||||
|
if(filesize >= 0)
|
||||||
|
{
|
||||||
|
std::cout << "File found: " << filename << " Size: " << filesize << std::endl << "----" << std::endl;
|
||||||
|
uint8_t* ByteArray;
|
||||||
|
ByteArray = new uint8_t[filesize + 1];
|
||||||
|
pakfs.getFile(filename, ByteArray);
|
||||||
|
ByteArray[filesize] = 0;
|
||||||
|
std::cout << ByteArray << std::endl << "----"<< std::endl;
|
||||||
|
delete [] ByteArray;
|
||||||
|
}else{
|
||||||
|
std::cout << "File not found: " << filename << std::endl << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user