initial commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
bin/
|
||||
asset/
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
.PHONY: all build clean lib
|
||||
|
||||
CXX = g++
|
||||
WSL = wsl --shell-type login
|
||||
EMXX = $(WSL) em++
|
||||
EMAR = $(WSL) emar
|
||||
|
||||
CXXFLAGS = -std=c++20 -O2
|
||||
|
||||
SRC_FILES = ./src/main.cpp \
|
||||
./src/lib.cpp
|
||||
|
||||
|
||||
LIB_FILES = ./src/lib.cpp
|
||||
|
||||
all: build-lib build
|
||||
|
||||
run: build
|
||||
./bin/assetpacker.exe
|
||||
|
||||
build-lib:
|
||||
@mkdir -p bin
|
||||
@mkdir -p bin/include/asset_packer
|
||||
@cp -r ./src/include/* ./bin/include/asset_packer
|
||||
$(CXX) $(CXXFLAGS) -I./src/include -c $(LIB_FILES) -o ./bin/asset_packer.o
|
||||
ar rcs ./bin/libassetpacker.a ./bin/asset_packer.o
|
||||
|
||||
build-lib-web:
|
||||
@mkdir -p bin
|
||||
@mkdir -p bin/include/asset_packer
|
||||
@cp -r ./src/include/* ./bin/include/asset_packer
|
||||
$(EMXX) $(CXXFLAGS) -I./src/include -c $(LIB_FILES) -o ./bin/asset_packer.web.o
|
||||
$(EMAR) rcs ./bin/libassetpacker.web.a ./bin/asset_packer.web.o
|
||||
|
||||
build:
|
||||
@mkdir -p bin
|
||||
@cp -r ./asset/ ./bin/
|
||||
$(CXX) $(CXXFLAGS) $(SRC_FILES) -I./src/include -o ./bin/assetpacker.exe
|
||||
|
||||
compile_commands.json: clean
|
||||
@echo "Generating compile_commands.json..."
|
||||
bear -- $(MAKE)
|
||||
|
||||
clean:
|
||||
@rm -r -f ./bin
|
||||
@rm -f compile_commands.json
|
||||
|
||||
test:
|
||||
@mkdir -p bin
|
||||
@cp -r ./asset/ ./bin/stuff/
|
||||
$(CXX) $(CXXFLAGS) -I./src/include $(LIB_FILES) ./src/test.cpp -o ./bin/test.exe
|
||||
./bin/test.exe
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// asset.hpp
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
// Struct size is exactly 80 bytes
|
||||
struct AssetMetadata {
|
||||
char name[64]; // Fixed size string for the filename
|
||||
uint64_t offset; // Where the raw file bytes begin in the blob
|
||||
uint64_t size; // How many bytes long the file is
|
||||
};
|
||||
|
||||
class AssetReader{
|
||||
private:
|
||||
std::string blob_path;
|
||||
std::unordered_map<std::string,AssetMetadata> index_table;
|
||||
public:
|
||||
AssetReader(const std::string& path): blob_path(path){}
|
||||
~AssetReader() = default;
|
||||
bool load_index();
|
||||
std::vector<uint8_t> extract_asset(const std::string& asset_name);
|
||||
|
||||
};
|
||||
|
||||
class AssetPacker{
|
||||
public:
|
||||
AssetPacker() = default;
|
||||
~AssetPacker() = default;
|
||||
void pack_assets(const std::string& output_blob_path, const std::vector<std::string>& files_to_pack);
|
||||
};
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// lib.cpp
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <asset.hpp>
|
||||
|
||||
|
||||
bool AssetReader::load_index(){
|
||||
std::ifstream blob(blob_path,std::ios::binary);
|
||||
if(!blob) return false;
|
||||
|
||||
uint32_t file_count = 0;
|
||||
blob.read(reinterpret_cast<char*>(&file_count),sizeof(file_count));
|
||||
for (uint32_t i = 0; i < file_count; i++){
|
||||
AssetMetadata meta;
|
||||
blob.read(reinterpret_cast<char*>(&meta), sizeof(AssetMetadata));
|
||||
index_table[std::string(meta.name)] = meta;
|
||||
}
|
||||
|
||||
blob.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> AssetReader::extract_asset(const std::string& asset_name){
|
||||
if (!index_table.contains(asset_name)){
|
||||
std::cerr << "Asset not found in blob: " << asset_name << "\n";
|
||||
return {};
|
||||
}
|
||||
AssetMetadata meta = index_table[asset_name];
|
||||
std::ifstream blob(blob_path,std::ios::binary);
|
||||
|
||||
blob.seekg(meta.offset);
|
||||
std::vector<uint8_t> buffer(meta.size);
|
||||
blob.read(reinterpret_cast<char*>(buffer.data()),meta.size);
|
||||
blob.close();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AssetPacker::pack_assets(const std::string& output_blob_path, const std::vector<std::string>& files_to_pack){
|
||||
std::ofstream blob(output_blob_path,std::ios::binary);
|
||||
if (!blob){std::cerr << "Failed to create blob file!" << std::endl; return;}
|
||||
|
||||
uint32_t file_count = files_to_pack.size();
|
||||
blob.write(reinterpret_cast<char*>(&file_count), sizeof(file_count));
|
||||
// Write how many files there are
|
||||
|
||||
|
||||
uint64_t current_data_offset = sizeof(file_count) + (file_count * sizeof(AssetMetadata)); // create and offset pointer for the number of file and our files metadata
|
||||
|
||||
std::vector<AssetMetadata> metadata_table;
|
||||
|
||||
for (auto &file_path : files_to_pack){
|
||||
|
||||
std::ifstream file(file_path,std::ios::binary | std::ios::ate);
|
||||
|
||||
if (!file){std::cerr << "Skipping missing file " << file_path << std::endl; continue; }
|
||||
|
||||
uint64_t file_size = file.tellg();
|
||||
|
||||
// create metadata
|
||||
AssetMetadata meta{};
|
||||
size_t length = file_path.copy(meta.name, sizeof(meta.name) - 1);
|
||||
meta.name[length] = '\0'; // Explicitly guarantee null-termination
|
||||
|
||||
meta.offset = current_data_offset;
|
||||
meta.size = file_size;
|
||||
|
||||
metadata_table.push_back(meta);
|
||||
current_data_offset += file_size;
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
// write meta data
|
||||
for (const auto& meta : metadata_table) {
|
||||
blob.write(reinterpret_cast<const char*>(&meta), sizeof(AssetMetadata));
|
||||
}
|
||||
|
||||
// second pass
|
||||
for (const auto& file_path: files_to_pack){
|
||||
std::ifstream file (file_path,std::ios::binary);
|
||||
if (!file) continue;
|
||||
blob << file.rdbuf();
|
||||
file.close();
|
||||
}
|
||||
|
||||
for (auto meta :metadata_table){
|
||||
std:: cout << std::format("stored {}",meta.name) << std::endl;
|
||||
}
|
||||
blob.close();
|
||||
std::cout << "Successfully packed " << metadata_table.size() << " files into " << output_blob_path << "\n";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <asset.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
int main(int argc, const char** argv){
|
||||
|
||||
std::string asset_folder_path;
|
||||
std::string blob_path;
|
||||
|
||||
if (argc == 1){
|
||||
std::cerr << argc-1 << " arguments found " << std::endl;
|
||||
std::cerr << "Format: ./asset_packer ASSET_FOLDER_PATH [BLOB_PATH]" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (std::string(argv[1]) == "--help" ||std::string(argv[1]) == "--help" ){
|
||||
std::cerr << "Format: ./asset_packer ASSET_FOLDER_PATH [BLOB_PATH] " << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc == 2){
|
||||
asset_folder_path = argv[1];
|
||||
blob_path = "data.dat";
|
||||
}
|
||||
|
||||
else if (argc == 3){
|
||||
blob_path = argv[2];
|
||||
}
|
||||
|
||||
std::vector<std::string> files_to_pack = {};
|
||||
|
||||
fs::path target_path = asset_folder_path;
|
||||
if (fs::exists(target_path) && fs::is_directory(target_path)){
|
||||
for (const auto& entry : fs::directory_iterator(target_path)){
|
||||
if(entry.is_regular_file()){
|
||||
files_to_pack.push_back(entry.path().relative_path().string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssetPacker asset_packer;
|
||||
asset_packer.pack_assets(blob_path, files_to_pack);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
// Include your library headers here
|
||||
#include <asset.hpp>
|
||||
|
||||
void run_asset_pipeline_tests() {
|
||||
std::cout << "[TEST] Initializing Asset Pipeline Tests...\n";
|
||||
|
||||
// Setup temporary testing files
|
||||
std::string test_blob = "test_data.dat";
|
||||
std::string text_file = "test_config.json";
|
||||
std::string fake_png = "test_player.png";
|
||||
|
||||
// 1. Create dummy files on disk to pack
|
||||
std::ofstream f1(text_file);
|
||||
f1 << "{ \"speed\": 250 }";
|
||||
f1.close();
|
||||
|
||||
std::ofstream f2(fake_png, std::ios::binary);
|
||||
f2 << "FAKE_PNG_BINARY_DATA_HEADER";
|
||||
f2.close();
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// TEST 1: Packing Verification
|
||||
// -------------------------------------------------------------
|
||||
AssetPacker packer;
|
||||
std::vector<std::string> files_to_pack = { text_file, fake_png };
|
||||
|
||||
// This should run without throwing errors or truncating files
|
||||
packer.pack_assets(test_blob, files_to_pack);
|
||||
assert(std::filesystem::exists(test_blob) && "FAIL: Blob file was not created!");
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// TEST 2: Reading and Integrity Verification (Round-Trip)
|
||||
// -------------------------------------------------------------
|
||||
AssetReader reader(test_blob);
|
||||
bool index_loaded = reader.load_index();
|
||||
assert(index_loaded && "FAIL: Reader could not parse blob index!");
|
||||
|
||||
// Extract the JSON back out
|
||||
std::vector<uint8_t> extracted_json_bytes = reader.extract_asset(text_file);
|
||||
std::string extracted_json(extracted_json_bytes.begin(), extracted_json_bytes.end());
|
||||
|
||||
// Assert that the file contents match exactly what we put in
|
||||
assert(extracted_json == "{ \"speed\": 250 }" && "FAIL: Extracted data does not match original data!");
|
||||
std::cout << " -> Pass: Data round-trip integrity verified.\n";
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// TEST 3: Graceful Missing Asset Handling
|
||||
// -------------------------------------------------------------
|
||||
std::vector<uint8_t> missing = reader.extract_asset("non_existent_file.png");
|
||||
assert(missing.empty() && "FAIL: Requesting a missing file should return an empty buffer!");
|
||||
std::cout << " -> Pass: Missing file boundaries handled gracefully.\n";
|
||||
|
||||
// Clean up test file clutter from the hard drive
|
||||
std::filesystem::remove(text_file);
|
||||
std::filesystem::remove(fake_png);
|
||||
std::filesystem::remove(test_blob);
|
||||
|
||||
std::cout << "[TEST] ALL ASSET PIPELINE TESTS PASSED SUCCESSFULY!\n\n";
|
||||
}
|
||||
|
||||
int main() {
|
||||
run_asset_pipeline_tests();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user