cod.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       cod.cc
00003 ///             COD file API
00004 ///
00005 
00006 /*
00007     Copyright (C) 2009-2012, Net Direct Inc. (http://www.netdirect.ca/)
00008     Copyright (C) 2008-2009, Nicolas VIVIEN
00009     Copyright (C) 2009, Josh Kropf
00010 
00011     This program is free software; you can redistribute it and/or modify
00012     it under the terms of the GNU General Public License as published by
00013     the Free Software Foundation; either version 2 of the License, or
00014     (at your option) any later version.
00015 
00016     This program is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020     See the GNU General Public License in the COPYING file at the
00021     root directory of this project for more details.
00022 */
00023 
00024 #include "cod.h"
00025 #include "cod-internal.h"
00026 #include "error.h"
00027 #include "endian.h"
00028 #include <config.h>
00029 #include <sys/types.h>
00030 #include <sys/stat.h>
00031 #include <unistd.h>
00032 #include <string>
00033 #include <string.h>
00034 
00035 #ifdef HAVE_ZLIB
00036  #include <zlib.h>
00037 #endif
00038 
00039 using namespace std;
00040 
00041 namespace Barry {
00042 
00043 
00044 size_t SeekNextCod(std::istream &input)
00045 {
00046         unsigned char codtype_simple[] = CODFILE_TYPE_SIMPLE;
00047         char codtype_pkzip[] = CODFILE_TYPE_PKZIP;
00048         char local_file_sig[] = PKZIP_LOCAL_FILE_SIG;
00049         char directory_sig[] = PKZIP_DIRECTORY_SIG;
00050 
00051         char signature[4];
00052 
00053         // finished reading stream containing single cod file
00054         input.peek();
00055         if( input.eof() ) {
00056                 return 0;
00057         }
00058 
00059         if( input.read(signature, sizeof(signature)).eof() ) {
00060                 throw Error("SeekNextCod: EOF while reading file signature");
00061         }
00062 
00063         if( memcmp(signature, codtype_pkzip, sizeof(codtype_pkzip)) == 0 ) {
00064 
00065                 if( memcmp(signature, local_file_sig, sizeof(signature)) == 0 ) {
00066                         pkzip_local_header_t header;
00067 
00068                         if( input.read((char *)&header, sizeof(pkzip_local_header_t)).eof() ) {
00069                                 throw Error("SeekNextCod: EOF while reading PKZIP header");
00070                         }
00071 
00072                         // skip cod file name and extra field, we don't need them
00073                         size_t skip_len = header.file_name_length + header.extra_field_length;
00074                         if( input.ignore(skip_len).eof() ) {
00075                                 throw Error("SeekNextCod: EOF while skipping unused fields");
00076                         }
00077 
00078                         return btohl(header.compressed_size);
00079                 }
00080                 else if( memcmp(signature, directory_sig, sizeof(signature)) == 0 ) {
00081                         // done reading when central directory is reached
00082                         return 0;
00083                 }
00084         }
00085         else if( memcmp(signature, codtype_simple, sizeof(codtype_simple)) == 0 ) {
00086                 // find and return size of cod file
00087                 if( input.seekg(0, ios::end).fail() ) {
00088                         throw Error("SeekNextCod: seek to end failed");
00089                 }
00090 
00091                 uint32_t size = input.tellg();
00092 
00093                 if( input.seekg(0, ios::beg).fail() ) {
00094                         throw Error("SeekNextCod: seek to start failed");
00095                 }
00096 
00097                 return size;
00098         }
00099         else {
00100                 throw Error("SeekNextCod: unknown COD file signature");
00101         }
00102 
00103         return 0;
00104 }
00105 
00106 
00107 CodFileBuilder::CodFileBuilder(const std::string &module_name, size_t module_count)
00108         : m_module_name(module_name)
00109         , m_module_count(module_count)
00110         , m_current_module(0)
00111 {
00112 }
00113 
00114 CodFileBuilder::~CodFileBuilder()
00115 {
00116 }
00117 
00118 void CodFileBuilder::WriteNextHeader(std::ostream &output, const uint8_t* module_buffer, uint32_t module_size)
00119 {
00120         // ignored for single module .cod files (simple .cod file)
00121         if( m_module_count == 1 ) {
00122                 return;
00123         }
00124 
00125         // 32bit CRC of module in file header and directory entry
00126         // using zero for CRC will result in warnings when inflating .cod file
00127         uint32_t crc = 0;
00128 
00129 #ifdef HAVE_ZLIB
00130         crc = crc32(0, NULL, module_size);
00131         crc = crc32(crc, module_buffer, module_size);
00132 #endif
00133 
00134         // .cod file name for siblings have hyphenated index number, name-1.cod
00135         std::ostringstream file_name(m_module_name, ios::app);
00136         if( m_current_module == 0 )
00137                 file_name << ".cod";
00138         else
00139                 file_name << "-" << m_current_module << ".cod";
00140 
00141         // current stream pointer is relative offset to start of file entry
00142         uint32_t entry_offset = output.tellp();
00143 
00144         // structures for the local file entry and central directory entry
00145         pkzip_local_header_t header;
00146         pkzip_directory_t entry;
00147 
00148         // zero both structs, most fields are zero
00149         memset(&header, 0, sizeof(pkzip_local_header_t));
00150         memset(&entry, 0, sizeof(pkzip_directory_t));
00151 
00152         char header_sig[] = PKZIP_LOCAL_FILE_SIG;
00153         output.write(header_sig, sizeof(header_sig));
00154 
00155         // version is always 0x00A0 = 'Windows NTFS'
00156         header.version_needed = htobs(10);
00157 
00158         // time and date fields seem to randomly have invalid or fixed values
00159         // just leave them as zero
00160         //header.last_mod_time
00161         //header.last_mod_date
00162 
00163         header.crc_32 = htobl(crc);
00164         header.compressed_size = htobl(module_size);
00165         header.uncompressed_size = htobl(module_size);
00166         header.file_name_length = htobs(file_name.str().length());
00167 
00168         // the very first cod sibling to be written has an extra field
00169         // length equal to 4, with all zeros in the field itself
00170         // all subsequent siblings have a zero length extra field
00171         //header.extra_field_length = htobs(4);
00172 
00173         output.write((char *)&header, sizeof(pkzip_local_header_t));
00174         output << file_name.str();
00175 
00176         char footer_sig[] = PKZIP_DIRECTORY_SIG;
00177 
00178         // version is always 0x00A0 = 'Windows NTFS'
00179         entry.version_madeby = htobs(10);
00180         entry.version_needed = htobs(10);
00181 
00182         entry.crc_32 = htobl(crc);
00183         entry.compressed_size = htobl(module_size);
00184         entry.uncompressed_size = htobl(module_size);
00185         entry.file_name_length = htobs(file_name.str().length());
00186         entry.relative_offset = htobl(entry_offset);
00187 
00188         m_directory.write(footer_sig, sizeof(footer_sig));
00189         m_directory.write((char*)&entry, sizeof(pkzip_directory_t));
00190         m_directory << file_name.str();
00191 
00192         m_current_module ++;
00193 }
00194 
00195 void CodFileBuilder::WriteFooter(std::ostream &output)
00196 {
00197         // ignored for single module .cod files (simple .cod file)
00198         if( m_module_count == 1 ) {
00199                 return;
00200         }
00201 
00202         pkzip_end_directory_t end;
00203         memset(&end, 0, sizeof(pkzip_end_directory_t));
00204 
00205         end.this_disk_entry_count = htobs(m_current_module);
00206         end.total_entry_count = htobs(m_current_module);
00207         end.directory_length = htobl(m_directory.str().length());
00208 
00209         // current stream pointer is relative offset to start of directory
00210         end.directory_offset = output.tellp();
00211 
00212         char sig[] = PKZIP_END_DIRECTORY_SIG;
00213 
00214         output.write(m_directory.str().data(), m_directory.str().length());
00215         output.write(sig, sizeof(sig));
00216         output.write((char *)&end, sizeof(pkzip_end_directory_t));
00217 }
00218 
00219 
00220 } // namespace Barry
00221