📄 jma.cpp
字号:
/*Copyright (C) 2005-2007 NSRT Team ( http://nsrt.edgeemu.com )This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseversion 2 as published by the Free Software Foundation.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include <sstream>#include "jma.h"using namespace std;#include "portable.h"#include "7z.h"#include "crc32.h"namespace JMA{ const char jma_magic[] = { 'J', 'M', 'A', 0, 'N' }; const unsigned int jma_header_length = 5; const unsigned char jma_version = 1; const unsigned int jma_version_length = 1; const unsigned int jma_total_header_length = jma_header_length + jma_version_length + UINT_SIZE; //Convert DOS/zip/JMA integer time to to time_t time_t uint_to_time(unsigned short date, unsigned short time) { tm formatted_time; formatted_time.tm_mday = date & 0x1F; formatted_time.tm_mon = ((date >> 5) & 0xF) - 1; formatted_time.tm_year = ((date >> 9) & 0x7f) + 80; formatted_time.tm_sec = (time & 0x1F) * 2; formatted_time.tm_min = (time >> 5) & 0x3F; formatted_time.tm_hour = (time >> 11) & 0x1F; return(mktime(&formatted_time)); } //Retreive the file block, what else? void jma_open::retrieve_file_block() throw(jma_errors) { unsigned char uint_buffer[UINT_SIZE]; unsigned char ushort_buffer[USHORT_SIZE]; //File block size is the last UINT in the file stream.seekg(-UINT_SIZE,ios::end); stream.read((char *)uint_buffer, UINT_SIZE); size_t file_block_size = charp_to_uint(uint_buffer); //Currently at the end of the file, so that's the file size size_t jma_file_size = stream.tellg(); //The file block can't be larger than the JMA file without it's header. //This if can probably be improved if (file_block_size >= jma_file_size-jma_total_header_length) { throw(JMA_BAD_FILE); } //Seek to before file block so we can read the file block stream.seekg(-((int)file_block_size+UINT_SIZE),ios::end); //This is needed if the file block is compressed stringstream decompressed_file_block; //Pointer to where to read file block from (file or decompressed buffer) istream *file_block_stream; //Setup file info buffer and byte to read with jma_file_info file_info; char byte; stream.get(byte); if (!byte) //If file block is compressed { //Compressed size isn't counting the byte we just read or the UINT for compressed size size_t compressed_size = file_block_size - (1+UINT_SIZE); //Read decompressed size / true file block size stream.read((char *)uint_buffer, UINT_SIZE); file_block_size = charp_to_uint(uint_buffer); //Setup access methods for decompression ISequentialInStream_Istream compressed_data(stream); ISequentialOutStream_Ostream decompressed_data(decompressed_file_block); //Decompress the data if (!decompress_lzma_7z(compressed_data, compressed_size, decompressed_data, file_block_size)) { throw(JMA_DECOMPRESS_FAILED); } //Go to beginning, setup pointer to buffer decompressed_file_block.seekg(0, ios::beg); file_block_stream = &decompressed_file_block; } else { stream.putback(byte); //Putback byte, byte is part of filename, not compressed indicator file_block_stream = &stream; } //Minimum file name length is 2 bytes, a char and a null //Minimum comment length is 1 byte, a null //There are currently 2 UINTs and 2 USHORTs per file while (file_block_size >= 2+1+UINT_SIZE*2+USHORT_SIZE*2) //This does allow for a gap, but that's okay { //First stored in the file block is the file name null terminated file_info.name = ""; file_block_stream->get(byte); while (byte) { file_info.name += byte; file_block_stream->get(byte); } //There must be a file name or the file is bad if (!file_info.name.length()) { throw(JMA_BAD_FILE); } //Same trick as above for the comment file_info.comment = ""; file_block_stream->get(byte); while (byte) { file_info.comment += byte; file_block_stream->get(byte); } //Next is a UINT representing the file's size file_block_stream->read((char *)uint_buffer, UINT_SIZE); file_info.size = charp_to_uint(uint_buffer); //Followed by CRC32 file_block_stream->read((char *)uint_buffer, UINT_SIZE); file_info.crc32 = charp_to_uint(uint_buffer); //Special USHORT representation of file's date file_block_stream->read((char *)ushort_buffer, USHORT_SIZE); file_info.date = charp_to_ushort(ushort_buffer); //Special USHORT representation of file's time file_block_stream->read((char *)ushort_buffer, USHORT_SIZE); file_info.time = charp_to_ushort(ushort_buffer); file_info.buffer = 0; //Pointing to null till we decompress files files.push_back(file_info); //Put file info into our structure //Subtract size of the file info we just read file_block_size -= file_info.name.length()+file_info.comment.length()+2+UINT_SIZE*2+USHORT_SIZE*2; } } //Constructor for opening JMA files for reading jma_open::jma_open(const char *compressed_file_name) throw (jma_errors) { decompressed_buffer = 0; compressed_buffer = 0; stream.open(compressed_file_name, ios::in | ios::binary); if (!stream.is_open()) { throw(JMA_NO_OPEN); } //Header is "JMA\0N" unsigned char header[jma_header_length]; stream.read((char *)header, jma_header_length); if (memcmp(jma_magic, header, jma_header_length)) { throw(JMA_BAD_FILE); } //Not the cleanest code but logical stream.read((char *)header, 5); if (*header <= jma_version) { chunk_size = charp_to_uint(header+1); //Chunk size is a UINT that follows version # retrieve_file_block(); } else { throw(JMA_UNSUPPORTED_VERSION); } } //Destructor only has to close the stream if neccesary jma_open::~jma_open() { if (stream.is_open()) { stream.close(); } } //Return a vector containing useful info about the files in the JMA vector<jma_public_file_info> jma_open::get_files_info() { vector<jma_public_file_info> file_info_vector; jma_public_file_info file_info; for (vector<jma_file_info>::iterator i = files.begin(); i != files.end(); i++) { file_info.name = i->name; file_info.comment = i->comment; file_info.size = i->size; file_info.datetime = uint_to_time(i->date, i->time); file_info.crc32 = i->crc32; file_info_vector.push_back(file_info); } return(file_info_vector); } //Skip forward a given number of chunks void jma_open::chunk_seek(unsigned int chunk_num) throw(jma_errors) { //Check the stream is open if (!stream.is_open()) { throw(JMA_NO_OPEN); } //Clear possible errors so the seek will work stream.clear(); //Move forward over header stream.seekg(jma_total_header_length, ios::beg); unsigned char int4_buffer[UINT_SIZE]; while (chunk_num--) { //Read in size of chunk stream.read((char *)int4_buffer, UINT_SIZE); //Skip chunk plus it's CRC32 stream.seekg(charp_to_uint(int4_buffer)+UINT_SIZE, ios::cur); } } //Return a vector of pointers to each file in the JMA, the buffer to hold all the files //must be initilized outside. vector<unsigned char *> jma_open::get_all_files(unsigned char *buffer) throw(jma_errors) { //If there's no stream we can't read from it, so exit if (!stream.is_open()) { throw(JMA_NO_OPEN); } //Seek to the first chunk chunk_seek(0); //Set the buffer that decompressed data goes to decompressed_buffer = buffer; //If the JMA is not solid if (chunk_size) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -