Removed multi-unget call in cod.cc -- only one unget() is guaranteed in C++
[barry.git] / src / cod.cc
blobe9be37f6f922f2af22f072e8a1a9afde477e6be1
1 ///
2 /// \file cod.cc
3 /// COD file API
4 ///
6 /*
7 Copyright (C) 2009, Net Direct Inc. (http://www.netdirect.ca/)
8 Copyright (C) 2008-2009, Nicolas VIVIEN
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "cod.h"
24 #include "cod-internal.h"
25 #include "error.h"
26 #include "endian.h"
27 #include <config.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <string>
32 #include <string.h>
34 #ifdef HAVE_ZLIB
35 #include <zlib.h>
36 #endif
38 using namespace std;
40 namespace Barry {
43 size_t SeekNextCod(std::istream &input)
45 char local_file_sig[] = PKZIP_LOCAL_FILE_SIG;
46 char directory_sig[] = PKZIP_DIRECTORY_SIG;
48 char signature[4];
50 // finished reading stream containing single cod file
51 input.peek();
52 if( input.eof() ) {
53 return 0;
56 if( input.read(signature, sizeof(signature)).eof() ) {
57 throw Error("SeekNextCod: EOF while reading file signature");
60 if( memcmp(signature, local_file_sig, sizeof(signature)) == 0 ) {
61 pkzip_local_header_t header;
63 if( input.read((char *)&header, sizeof(pkzip_local_header_t)).eof() ) {
64 throw Error("SeekNextCod: EOF while reading PKZIP header");
67 // skip cod file name and extra field, we don't need them
68 size_t skip_len = header.file_name_length + header.extra_field_length;
69 if( input.ignore(skip_len).eof() ) {
70 throw Error("SeekNextCod: EOF while skipping unused fields");
73 return btohl(header.compressed_size);
75 else if( memcmp(signature, directory_sig, sizeof(signature)) == 0 ) {
76 // done reading when central directory is reached
77 return 0;
79 else {
80 // find and return size of cod file
81 if( input.seekg(0, ios::end).fail() ) {
82 throw Error("SeekNextCod: seek to end failed");
85 uint32_t size = input.tellg();
87 if( input.seekg(0, ios::beg).fail() ) {
88 throw Error("SeekNextCod: seek to start failed");
91 return size;
96 CodFileBuilder::CodFileBuilder(const std::string &module_name, size_t module_count)
97 : m_module_name(module_name)
98 , m_module_count(module_count)
99 , m_current_module(0)
103 CodFileBuilder::~CodFileBuilder()
107 void CodFileBuilder::WriteNextHeader(std::ostream &output, const uint8_t* module_buffer, uint32_t module_size)
109 // ignored for single module .cod files (simple .cod file)
110 if( m_module_count == 1 ) {
111 return;
114 // 32bit CRC of module in file header and directory entry
115 // using zero for CRC will result in warnings when inflating .cod file
116 uint32_t crc = 0;
118 #ifdef HAVE_ZLIB
119 crc = crc32(0, NULL, module_size);
120 crc = crc32(crc, module_buffer, module_size);
121 #endif
123 // .cod file name for siblings have hyphenated index number, name-1.cod
124 std::ostringstream file_name(m_module_name, ios::app);
125 if( m_current_module == 0 )
126 file_name << ".cod";
127 else
128 file_name << "-" << m_current_module << ".cod";
130 // current stream pointer is relative offset to start of file entry
131 uint32_t entry_offset = output.tellp();
133 // structures for the local file entry and central directory entry
134 pkzip_local_header_t header;
135 pkzip_directory_t entry;
137 // zero both structs, most fields are zero
138 memset(&header, 0, sizeof(pkzip_local_header_t));
139 memset(&entry, 0, sizeof(pkzip_directory_t));
141 char header_sig[] = PKZIP_LOCAL_FILE_SIG;
142 output.write(header_sig, sizeof(header_sig));
144 // version is always 0x00A0 = 'Windows NTFS'
145 header.version_needed = htobs(10);
147 // time and date fields seem to randomly have invalid or fixed values
148 // just leave them as zero
149 //header.last_mod_time
150 //header.last_mod_date
152 header.crc_32 = htobl(crc);
153 header.compressed_size = htobl(module_size);
154 header.uncompressed_size = htobl(module_size);
155 header.file_name_length = htobs(file_name.str().length());
157 // the very first cod sibling to be written has an extra field
158 // length equal to 4, with all zeros in the field itself
159 // all subsequent siblings have a zero length extra field
160 //header.extra_field_length = htobs(4);
162 output.write((char *)&header, sizeof(pkzip_local_header_t));
163 output << file_name.str();
165 char footer_sig[] = PKZIP_DIRECTORY_SIG;
167 // version is always 0x00A0 = 'Windows NTFS'
168 entry.version_madeby = htobs(10);
169 entry.version_needed = htobs(10);
171 entry.crc_32 = htobl(crc);
172 entry.compressed_size = htobl(module_size);
173 entry.uncompressed_size = htobl(module_size);
174 entry.file_name_length = htobs(file_name.str().length());
175 entry.relative_offset = htobl(entry_offset);
177 m_directory.write(footer_sig, sizeof(footer_sig));
178 m_directory.write((char*)&entry, sizeof(pkzip_directory_t));
179 m_directory << file_name.str();
181 m_current_module ++;
184 void CodFileBuilder::WriteFooter(std::ostream &output)
186 // ignored for single module .cod files (simple .cod file)
187 if( m_module_count == 1 ) {
188 return;
191 char sig[] = PKZIP_END_DIRECTORY_SIG;
193 pkzip_end_directory_t end;
194 memset(&end, 0, sizeof(pkzip_end_directory_t));
196 end.this_disk_entry_count = htobs(m_current_module);
197 end.total_entry_count = htobs(m_current_module);
198 end.directory_length = htobl(m_directory.str().length());
200 // current stream pointer is relative offset to start of directory
201 end.directory_offset = output.tellp();
203 output.write(m_directory.str().c_str(), m_directory.str().length());
204 output.write(sig, sizeof(sig));
205 output.write((char *)&end, sizeof(pkzip_end_directory_t));
209 } // namespace Barry