Compare commits

...

3 Commits

Author SHA1 Message Date
Joca 14f0fc1f3a
Push Chunks Manipulator
Also commented out the shaderc build script, since it seems broken
on upstream.
2024-07-09 21:09:22 -03:00
BodgeMaster 61cec73c49 lib/zlibutil, tools/zlibutil: moving things around
This includes the following changes:
- Move the data type wrangling into the lib to make it easier to use
- Put the functions into their own `zlib` namespace
- Use ErrorOr instead of exceptions
- Error codes for compression/decompression
2024-06-10 20:41:29 -03:00
BodgeMaster ef0a2707dd lib/zlibutil, tools/zlibutil: moving things around
This includes the following changes:
- Move the data type wrangling into the lib to make it easier to use
- Put the functions into their own `zlib` namespace
- Use ErrorOr instead of exceptions
- Error codes for compression/decompression
2024-06-09 02:51:32 +02:00
6 changed files with 263 additions and 103 deletions

View File

@ -70,24 +70,24 @@ fi
popd >/dev/null 2>&1
echo ">>> done"
echo ">>> Dealing with shaderc shenanigans..."
pushd dependencies/tmp/shaderc
echo "Getting sources using the provided script..."
./update_shaderc_sources.py
SHADERC_BUILD="build-$(dd if=/dev/urandom bs=1 count=5 2>/dev/null | base32)"
echo "Creating and entering directory $SHADERC_BUILD."
mkdir "$SHADERC_BUILD"
cd "$SHADERC_BUILD"
echo "Running CMake..."
CXXFLAGS="-Wno-error" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../src/
echo "Running Ninja..."
ninja
popd >/dev/null 2>&1
#echo ">>> Dealing with shaderc shenanigans..."
#pushd dependencies/tmp/shaderc
#echo "Getting sources using the provided script..."
#./update_shaderc_sources.py
#SHADERC_BUILD="build-$(dd if=/dev/urandom bs=1 count=5 2>/dev/null | base32)"
#echo "Creating and entering directory $SHADERC_BUILD."
#mkdir "$SHADERC_BUILD"
#cd "$SHADERC_BUILD"
#echo "Running CMake..."
#CXXFLAGS="-Wno-error" cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../src/
#echo "Running Ninja..."
#ninja
#popd >/dev/null 2>&1
#if needed copy more relevant files to dependencies/shaderc
echo "Copying binary to dependencies/shaderc/bin..."
mkdir -vp dependencies/shaderc/bin
cp -v "dependencies/tmp/shaderc/$SHADERC_BUILD/glslc/glslc" dependencies/shaderc/bin
echo ">>> done"
#echo "Copying binary to dependencies/shaderc/bin..."
#mkdir -vp dependencies/shaderc/bin
#cp -v "dependencies/tmp/shaderc/$SHADERC_BUILD/glslc/glslc" dependencies/shaderc/bin
#echo ">>> done"
echo ">>> Building zlib..."
pushd dependencies/zlib-1.3.1/

View File

@ -17,6 +17,11 @@
#include <cstdint>
//TODO: needed macros:
// TRY: takes a variable, a function call, and the ErrorOr return type of the calling function - intended to automatically unwrap the ErrorOr data type or propagate the error upwards
// RAISE: takes an error code and optionally a message to produce something like `return ErrorOr<T>(true, errorCode, file, lineNumber, message)`
template <typename T>
struct ErrorOr {
bool isError;
@ -100,6 +105,9 @@ namespace ErrorCodes {
// when too much data is available
const uint8_t OVERFLOW = 13;
const uint8_t COMPRESSION = 14;
const uint8_t DECOMPRESSION = 15;
const uint8_t UNIMPLEMENTED = 254;
const uint8_t UNKNOWN = 255;

View File

@ -16,92 +16,101 @@
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include "../lib/zlibutil.hpp"
// includes vector and error.hpp
#include "zlibutil.hpp"
#include <bitset>
#include <iomanip>
#include <iostream>
#include <vector>
#include <tinyutf8/tinyutf8.h>
#include <zlib/zlib.h>
#include <cstring>
#include "../lib/cli.hpp"
#include "../lib/file.hpp"
#define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1
#define EXIT_USAGE 2
#include <iostream>
#include <fstream>
#include <vector>
#include <zlib.h>
#define CHUNK_SIZE 16384 // Chunk size
namespace zlib {
std::vector<char> compressData(const char* data, int size) {
z_stream zs;
memset(&zs, 0, sizeof(zs));
ErrorOr<std::vector<uint8_t>> compressData(std::vector<uint8_t> data) {
// I, too, love the fact that raw bytes are signed and therefore can have negative values. -_-
std::vector<int8_t> signedData = std::vector<int8_t>(data.begin(), data.end());
if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
throw(std::runtime_error("deflateInit failed while compressing."));
z_stream zs;
memset(&zs, 0, sizeof(zs));
zs.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data));
zs.avail_in = size;
if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::COMPRESSION);
//TODO: include error message once implemented
//throw(std::runtime_error("deflateInit failed while compressing."));
}
int ret;
char outbuffer[CHUNK_SIZE];
std::vector<char> compressedData;
zs.next_in = reinterpret_cast<Bytef*>(reinterpret_cast<char*>(signedData.data()));
zs.avail_in = signedData.size();
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = CHUNK_SIZE;
int ret;
char outbuffer[CHUNK_SIZE];
std::vector<char> compressedData;
ret = deflate(&zs, Z_FINISH);
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = CHUNK_SIZE;
if (compressedData.size() < zs.total_out)
compressedData.insert(compressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out);
} while (ret == Z_OK);
ret = deflate(&zs, Z_FINISH);
deflateEnd(&zs);
if (compressedData.size() < zs.total_out)
compressedData.insert(compressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out);
} while (ret == Z_OK);
if (ret != Z_STREAM_END)
throw(std::runtime_error("Error while compressing: " + std::to_string(ret)));
deflateEnd(&zs);
if (ret != Z_STREAM_END) {
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::COMPRESSION);
//TODO: include error message once implemented
//throw(std::runtime_error("Error while compressing: " + std::to_string(ret)));
}
return ErrorOr<std::vector<uint8_t>>(std::vector<uint8_t>(compressedData.begin(), compressedData.end()));
}
ErrorOr<std::vector<uint8_t>> decompressData(std::vector<uint8_t> data) {
// I, too, love the fact that raw bytes are signed and therefore can have negative values. -_-
std::vector<int8_t> signedData = std::vector<int8_t>(data.begin(), data.end());
z_stream zs;
memset(&zs, 0, sizeof(zs));
if (inflateInit(&zs) != Z_OK) {
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::DECOMPRESSION);
//TODO: include error message once implemented
//throw(std::runtime_error("inflateInit failed while decompressing."));
}
zs.next_in = reinterpret_cast<Bytef*>(reinterpret_cast<char*>(signedData.data()));
zs.avail_in = signedData.size();
int ret;
char outbuffer[CHUNK_SIZE];
std::vector<char> decompressedData;
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = CHUNK_SIZE;
ret = inflate(&zs, 0);
if (decompressedData.size() < zs.total_out)
decompressedData.insert(decompressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out);
} while (ret == Z_OK);
inflateEnd(&zs);
if (ret != Z_STREAM_END) {
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::DECOMPRESSION);
//TODO: include error message once implemented
//throw(std::runtime_error("Error while decompressing: " + std::to_string(ret)));
}
return ErrorOr<std::vector<uint8_t>>(std::vector<uint8_t>(decompressedData.begin(), decompressedData.end()));
}
return compressedData;
}
std::vector<char> decompressData(const char* data, int size) {
z_stream zs;
memset(&zs, 0, sizeof(zs));
if (inflateInit(&zs) != Z_OK)
throw(std::runtime_error("inflateInit failed while decompressing."));
zs.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data));
zs.avail_in = size;
int ret;
char outbuffer[CHUNK_SIZE];
std::vector<char> decompressedData;
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = CHUNK_SIZE;
ret = inflate(&zs, 0);
if (decompressedData.size() < zs.total_out)
decompressedData.insert(decompressedData.end(), outbuffer, outbuffer + CHUNK_SIZE - zs.avail_out);
} while (ret == Z_OK);
inflateEnd(&zs);
if (ret != Z_STREAM_END)
throw(std::runtime_error("Error while decompressing: " + std::to_string(ret)));
return decompressedData;
}

View File

@ -16,12 +16,12 @@
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#pragma once
#include <vector>
#include "error.hpp"
#ifndef ZLIBUTIL_H
#define ZLIBUTIL_H
std::vector<char> compressData(const char* data, int size);
std::vector<char> decompressData(const char* data, int size);
#endif // ZLIBUTIL_H
namespace zlib {
ErrorOr<std::vector<uint8_t>> compressData(std::vector<uint8_t> data);
ErrorOr<std::vector<uint8_t>> decompressData(std::vector<uint8_t> data);
}

View File

@ -0,0 +1,131 @@
/*
Copyright 2024, FOSS-VG Developers and Contributers
Author(s):
Jocadbz
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
version 3 along with this program.
If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
*/
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <cstring>
using namespace std;
/*
* Ok, before you read this, please understand.
* This is not a elaborate program. We are not
* using fancy parsers.
* I'm straight up rawdogging the .mca files
* here. Of course this should be fixed, but
* don't get mad.
*/
// Define constants related to .mca file format
const int SECTOR_SIZE = 4096;
const int CHUNK_OFFSET = 4; // Offset where chunk data starts within a sector
const int CHUNK_SIZE = 4096; // Size of a chunk data block
const int HEADER_SIZE = 4;
const int TIMESTAMP_SIZE = 4;
const int NUM_CHUNKS_PER_REGION = 1024; // 32x32 chunks per region file
// Function to extract chunks from a .mca file
// Function to extract chunks from a .mca file and print position, offset, and timestamp
void extractChunks(const string& mcaFilename) {
ifstream file(mcaFilename, ios::binary);
if (!file.is_open()) {
cerr << "Error opening file: " << mcaFilename << endl;
return;
}
// Calculate the number of sectors in the file
file.seekg(0, ios::end);
int fileSize = file.tellg();
int numSectors = fileSize / SECTOR_SIZE;
// Print header information
cout << "Position (x, z), Offset, Timestamp:" << endl;
// Iterate through each sector
for (int sector = 0; sector < numSectors; ++sector) {
// Read the sector header (4 bytes)
file.seekg(sector * SECTOR_SIZE);
char header[HEADER_SIZE];
file.read(header, HEADER_SIZE);
// Extract position (x, z) from the sector header
int x = sector % NUM_CHUNKS_PER_REGION;
int z = sector / NUM_CHUNKS_PER_REGION;
// Calculate offset and timestamp positions
int offset = sector * SECTOR_SIZE + HEADER_SIZE;
int timestampOffset = sector * SECTOR_SIZE + HEADER_SIZE + CHUNK_SIZE;
// Read the timestamp (4 bytes) from the sector
file.seekg(timestampOffset);
char timestampBytes[TIMESTAMP_SIZE];
file.read(timestampBytes, TIMESTAMP_SIZE);
int timestamp = *reinterpret_cast<int*>(timestampBytes);
// Print formatted information
cout << "(" << x << ", " << z << "), " << offset << ", " << timestamp << endl;
}
file.close();
}
// Function to add or replace a chunk in a .mca file
void addReplaceChunk(const string& mcaFilename, int sectorIndex, const vector<uint8_t>& newChunkData) {
fstream file(mcaFilename, ios::in | ios::out | ios::binary);
if (!file.is_open()) {
cerr << "Error opening file: " << mcaFilename << endl;
return;
}
// Calculate the offset in the file where the chunk should be written
int fileOffset = sectorIndex * SECTOR_SIZE + CHUNK_OFFSET;
// Seek to the appropriate position in the file
file.seekp(fileOffset);
// Write the new chunk data
file.write(reinterpret_cast<const char*>(newChunkData.data()), newChunkData.size());
file.close();
cout << "Chunk added/replaced successfully at sector " << sectorIndex << endl;
}
int main() {
// TODO: Remember to implement CLI here.
string mcaFilename = "r.0.0.mca"; // Example .mca file path
// Example: Extract chunks from the .mca file
extractChunks(mcaFilename);
// Example: Add or replace a chunk in the .mca file
int sectorIndex = 0; // Example: Replace the chunk in the first sector
vector<uint8_t> newChunkData(CHUNK_SIZE, 0); // Example: New chunk data (all zeroes for demonstration)
addReplaceChunk(mcaFilename, sectorIndex, newChunkData);
extractChunks(mcaFilename);
return 0;
}

View File

@ -28,6 +28,7 @@
#include "../lib/cli.hpp"
#include "../lib/file.hpp"
#include "../lib/zlibutil.hpp"
#include "../lib/error.hpp"
#define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1
@ -112,14 +113,18 @@ int main(int argc, char* argv[]) {
return EXIT_RUNTIME;
}
File* file = filePointer.value;
File *writeFile;
File* writeFile;
if (cliParser.getFlag("decompress").value) {
std::vector<uint8_t> bytes = file->read(file->size.value).value; // this is what you get from lib/file IIRC
std::vector<char> differentBytes = std::vector<char>(bytes.begin(), bytes.end());
std::vector<uint8_t> bytes = file->read(file->size.value).value;
std::vector<char> compressed = decompressData(differentBytes.data(), file->size.value);
std::vector<unsigned char> unsigneddata = std::vector<unsigned char>(compressed.begin(), compressed.end());
ErrorOr<std::vector<uint8_t>> decompressed = zlib::decompressData(bytes);
if (decompressed.isError) {
std::cout << "Error: Failed to decompress: " << filename << std::endl;
delete file;
// not cleaning up writeFile here bc it hasn't been created
return EXIT_RUNTIME;
}
std::string outFilename;
if (filename.length() > 3 && filename.rfind(".zz") == filename.length()-3) {
@ -128,16 +133,23 @@ int main(int argc, char* argv[]) {
outFilename = filename + ".uncompressed";
}
writeFile = File::open(outFilename, 'w').value;
writeFile->write(unsigneddata);
writeFile->write(decompressed.value);
writeFile->close();
} else {
std::vector<uint8_t> bytes = file->read(file->size.value).value; // this is what you get from lib/file IIRC
std::vector<char> differentBytes = std::vector<char>(bytes.begin(), bytes.end());
std::vector<uint8_t> bytes = file->read(file->size.value).value;
std::vector<char> compressed = compressData(differentBytes.data(), file->size.value);
std::vector<unsigned char> unsigneddata = std::vector<unsigned char>(compressed.begin(), compressed.end());
ErrorOr<std::vector<uint8_t>> compressed = zlib::compressData(bytes);
if (compressed.isError) {
std::cout << "Error: Failed to compress: " << filename << std::endl;
delete file;
// not cleaning up writeFile here bc it hasn't been created
return EXIT_RUNTIME;
}
writeFile = File::open(filename + ".zz", 'w').value;
writeFile->write(unsigneddata);
writeFile->write(compressed.value);
writeFile->close();
}
delete file;
delete writeFile;
}