-
Notifications
You must be signed in to change notification settings - Fork 5
Add C++ port #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add C++ port #3
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,247 @@ | ||
| /* Stellar-CSO | ||
| A tool for converting ISO/XISO files to CSO (Compressed ISO) | ||
|
|
||
| Copyright 2018 David O'Rourke <[email protected]> | ||
| Copyright 2022 MakeMHz LLC <[email protected]> | ||
| Based on ciso from https://github.com/jamie/ciso | ||
| */ | ||
|
|
||
| #include <lz4frame.h> | ||
| #include <lz4hc.h> | ||
|
|
||
| #include <iostream> | ||
| #include <ios> | ||
| #include <filesystem> | ||
| #include <fstream> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| const uint32_t FATX_SIZE_LIMIT = 0xFFBF6000; | ||
|
|
||
| const std::string CISO_MAGIC = "CISO"; | ||
| const uint32_t CISO_HEADER_SIZE = 0x18; | ||
| const uint32_t CISO_BLOCK_SIZE = 0x800; | ||
| const uint32_t CISO_PLAIN_BLOCK = 0x80000000; | ||
|
|
||
| size_t image_offset = 0; | ||
|
|
||
| void detect_iso_type(std::ifstream& f) { | ||
| try { | ||
| f.seekg(0x18310000, std::ios::beg); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Magic offsets all over |
||
| } catch (std::exception const& e) { | ||
| std::cout << "File not big enough to be a Redump style image." << std::endl; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Why are we flushing on almost every cout, and some cerr's where we immediately exit, are we not trusting the OS? |
||
| } | ||
| if (f.tellg() == 0x18310000) { | ||
| std::string buf(20, '\0'); | ||
| f.read(&buf[0], 20); | ||
| if (buf == "MICROSOFT*XBOX*MEDIA") { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Used more than once should probably be a constexpr var |
||
| image_offset = 0x18300000; | ||
| return; | ||
| } | ||
| } | ||
| if (f.bad() || f.fail() || f.eof()) { | ||
| f.clear(); | ||
| } | ||
|
|
||
| try { | ||
| f.seekg(0x10000, std::ios::beg); | ||
| } catch (std::exception &e) { | ||
| std::cout << "File not big enough to be a raw XDVDFS image." << std::endl; | ||
| } | ||
| if (f.tellg() == 0x10000) { | ||
| std::string buf(20, '\0'); | ||
| f.read(&buf[0], 20); | ||
| if (buf == "MICROSOFT*XBOX*MEDIA") { | ||
| image_offset = 0; | ||
| return; | ||
| } else { | ||
| std::cout << buf << std::endl; | ||
| } | ||
| } | ||
| std::cerr << "Could not detect ISO type."<< std::endl; | ||
| exit(1); | ||
| } | ||
|
|
||
| class ciso { | ||
| private: | ||
| std::string magic; | ||
| uint32_t ver; | ||
| uint32_t block_size; | ||
| uint64_t total_bytes; | ||
| uint32_t total_blocks; | ||
| uint32_t align; | ||
| public: | ||
| ciso (uint64_t total_bytes) : magic(CISO_MAGIC), ver(2), block_size(CISO_BLOCK_SIZE), | ||
| total_bytes(total_bytes), align(2) { | ||
| total_blocks = total_bytes/CISO_BLOCK_SIZE; | ||
| } | ||
|
|
||
| ciso (std::ifstream &f) { | ||
| f.seekg(0, std::ios::end); | ||
| uint32_t file_size = (int32_t)f.tellg() - image_offset; | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems to be causing issues, should (probably) be
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That brings us closer at least; now redump input results in two files with reasonable sizes. Output doesn't match the python script past the header though... |
||
| *this = ciso(file_size); | ||
| f.seekg(0, std::ios::beg); | ||
| } | ||
|
|
||
| ciso (const char* filename) { | ||
| std::ifstream f(filename); | ||
| *this = ciso(f); | ||
| } | ||
|
|
||
| friend std::ostream& operator<<(std::ostream& os, ciso const& c); | ||
|
|
||
| uint32_t get_block_size() { return block_size; }; | ||
| uint32_t get_total_blocks() { return total_blocks; }; | ||
| uint32_t get_align() { return align; }; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Extra |
||
|
|
||
| void write_header(std::ofstream& f) { | ||
| f << magic; | ||
| f.write((char*)&CISO_HEADER_SIZE, sizeof(CISO_HEADER_SIZE)) | ||
| .write((char*)&total_bytes, sizeof(total_bytes)) | ||
| .write((char*)&block_size, sizeof(block_size)); | ||
| f << reinterpret_cast<char*>(&ver) << reinterpret_cast<char*>(&align); | ||
| f << "\0\0" << std::flush; | ||
| } | ||
|
|
||
| void write_block_index(std::ofstream& f, std::vector<uint32_t> const& block_index) { | ||
| for (uint32_t i: block_index) { | ||
| f.write((char*)&i, sizeof(i)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: C style casts throughout |
||
| } | ||
| } | ||
| }; | ||
|
|
||
| std::ostream& operator<<(std::ostream& os, ciso const& c) { | ||
| os << "Magic: " << c.magic << '\n' | ||
| << "Version: " << c.ver << '\n' | ||
| << "Block Size: " << c.block_size << '\n' | ||
| << "Total Bytes: " << c.total_bytes << '\n' | ||
| << "Total Blocks: " << c.total_blocks << '\n' | ||
| << "Alignment: " << c.align << std::endl; | ||
| return os; | ||
| } | ||
|
|
||
| void pad_file_size(std::ofstream& f) { | ||
| f.seekp(0, std::ios::end); | ||
| size_t size = f.tellp(); | ||
| std::string zero(0x400 - (size & 0x3FF), '\0'); | ||
| f << zero << std::flush; | ||
| } | ||
|
|
||
| void compress_iso(std::string &infile) { | ||
| LZ4F_cctx* cctxPtr; | ||
| auto ret = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION); | ||
| if (ret != 0) { | ||
| std::cerr << "CompressionContext creation failed, exiting..."; | ||
| exit(2); | ||
| } | ||
| std::ofstream fout_1(infile + ".1.cso", std::ios::binary); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Output file should truncate the |
||
| std::ofstream fout_2; | ||
|
|
||
| std::ifstream fin(infile, std::ios::binary); | ||
| if (!fin.is_open()) { | ||
| std::cerr << "ERROR: Could not open input file " << infile << "!" << std::endl; | ||
| exit(4); | ||
| } | ||
|
|
||
| std::cout << "Compressing " << infile << std::endl; | ||
| detect_iso_type(fin); | ||
| ciso ciso(fin); | ||
| fin.seekg(0, std::ios::beg); | ||
| std::cout << ciso; | ||
| ciso.write_header(fout_1); | ||
| std::vector<uint32_t> block_index(ciso.get_total_blocks() + 1, 0); | ||
| ciso.write_block_index(fout_1, block_index); | ||
|
|
||
| size_t write_pos = fout_1.tellp(); | ||
| uint32_t align_b = 1 << ciso.get_align(); | ||
| uint32_t align_m = align_b - 1; | ||
|
|
||
| std::string raw_data(ciso.get_block_size(), '\0'); | ||
| std::string compressed_data(ciso.get_block_size()+27, '\0'); | ||
| std::string alignment_buffer(64, '\0'); | ||
|
|
||
| std::string *writable_data; | ||
| std::ofstream *split_fout = &fout_1; | ||
|
|
||
| LZ4F_preferences_t* lz4pref = new LZ4F_preferences_t(); | ||
| lz4pref->frameInfo.blockMode = LZ4F_blockIndependent; | ||
| lz4pref->compressionLevel = LZ4HC_CLEVEL_MAX; | ||
| lz4pref->autoFlush = 1; | ||
|
|
||
| uint32_t block; | ||
|
|
||
| for (block = 0; block < ciso.get_total_blocks(); ++block) { | ||
| // Check if we need to split the ISO (due to FATX limitations) | ||
| if (write_pos > FATX_SIZE_LIMIT) { | ||
| fout_2.open(infile + ".2.cso", std::ios::binary); | ||
| split_fout = &fout_2; | ||
| write_pos = 0; | ||
| } | ||
|
|
||
| // Write alignment | ||
| uint32_t align = write_pos & align_m; | ||
| if (align != 0) { | ||
| align = align_b - align; | ||
| split_fout->write(alignment_buffer.c_str(), align); | ||
| write_pos += align; | ||
| } | ||
|
|
||
| // Mark offset index | ||
| block_index[block] = write_pos >> ciso.get_align(); | ||
|
|
||
| // Read raw data | ||
| auto raw_data_chunk_start = fin.tellg(); | ||
| fin.read(&raw_data[0], ciso.get_block_size()); | ||
| size_t data_size = fin.tellg() - raw_data_chunk_start; | ||
|
|
||
| // Compress all the things | ||
| LZ4F_compressBegin(cctxPtr, &compressed_data[0], compressed_data.size(), lz4pref); | ||
| auto compressed_size = LZ4F_compressUpdate(cctxPtr, | ||
| &compressed_data[0], compressed_data.size(), | ||
| &raw_data[0], data_size, | ||
| nullptr); | ||
| if (LZ4F_isError(compressed_size)) { | ||
| std::cerr << "ERROR: " << LZ4F_getErrorName(compressed_size) << std::endl; | ||
| exit(1); | ||
| } | ||
| LZ4F_flush(cctxPtr, &compressed_data[0], 0x800, nullptr); | ||
|
|
||
| // Make sure the data we write back actually saves space | ||
| if ((compressed_size + 12) >= data_size) { | ||
| writable_data = &raw_data; | ||
| write_pos += data_size; | ||
| } else { | ||
| writable_data = &compressed_data; | ||
| block_index[block] |= 0x80000000; | ||
| write_pos += compressed_size; | ||
| data_size = compressed_size; | ||
| } | ||
| split_fout->write(writable_data->c_str(), data_size); | ||
| } | ||
|
|
||
| fin.close(); | ||
| *block_index.rbegin() = write_pos >> ciso.get_align(); | ||
|
|
||
| fout_1.seekp(CISO_HEADER_SIZE, std::ios::beg); | ||
| ciso.write_block_index(fout_1, block_index); | ||
|
|
||
| pad_file_size(fout_1); | ||
| fout_1.close(); | ||
|
|
||
| if (split_fout == &fout_2) { | ||
| pad_file_size(fout_2); | ||
| fout_2.close(); | ||
| } | ||
|
|
||
| LZ4F_freeCompressionContext(cctxPtr); | ||
| } | ||
|
|
||
| int main(int argc, char* argv[]) { | ||
| if (argc < 2) { | ||
| std::cerr << "Usage:\n" << argv[0] << " <image file>" << std::endl; | ||
| return 1; | ||
| } | ||
| std::string file(argv[1]); | ||
| compress_iso(file); | ||
| return 0; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Above should probably be constexpr