First version

This commit is contained in:
stevenhowes
2024-10-30 08:34:11 +00:00
parent 51c983a782
commit e070ec044e
13 changed files with 336 additions and 1 deletions
+36
View File
@@ -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
+94
View File
@@ -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));
}
}
}
+29
View File
@@ -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
+57
View File
@@ -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);
}
+41
View File
@@ -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
+11 -1
View File
@@ -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).
+1
View File
@@ -0,0 +1 @@
this only exists within subfolders under base
+1
View File
@@ -0,0 +1 @@
this file only exists in the base folder
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
This is a test file that's not in a pak
+28
View File
@@ -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;
}
+37
View File
@@ -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;
}