Bumped copyright dates for 2013
[barry.git] / src / cod.cc
blob27476befe0a91528fcc866fec9a3aa818387d6b3
1 ///
2 /// \file cod.cc
3 /// COD file API
4 ///
6 /*
7 Copyright (C) 2009-2013, Net Direct Inc. (http://www.netdirect.ca/)
8 Copyright (C) 2008-2009, Nicolas VIVIEN
9 Copyright (C) 2009, Josh Kropf
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "i18n.h"
25 #include "cod.h"
26 #include "cod-internal.h"
27 #include "error.h"
28 #include "endian.h"
29 #include <config.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <string>
34 #include <string.h>
36 #ifdef HAVE_ZLIB
37 #include <zlib.h>
38 #endif
40 using namespace std;
42 namespace Barry {
45 size_t SeekNextCod(std::istream &input)
47 unsigned char codtype_simple[] = CODFILE_TYPE_SIMPLE;
48 char codtype_pkzip[] = CODFILE_TYPE_PKZIP;
49 char local_file_sig[] = PKZIP_LOCAL_FILE_SIG;
50 char directory_sig[] = PKZIP_DIRECTORY_SIG;
52 char signature[4];
54 // finished reading stream containing single cod file
55 input.peek();
56 if( input.eof() ) {
57 return 0;
60 if( input.read(signature, sizeof(signature)).eof() ) {
61 throw Error(_("SeekNextCod: EOF while reading file signature"));
64 if( memcmp(signature, codtype_pkzip, sizeof(codtype_pkzip)) == 0 ) {
66 if( memcmp(signature, local_file_sig, sizeof(signature)) == 0 ) {
67 pkzip_local_header_t header;
69 if( input.read((char *)&header, sizeof(pkzip_local_header_t)).eof() ) {
70 throw Error(_("SeekNextCod: EOF while reading PKZIP header"));
73 // skip cod file name and extra field, we don't need them
74 size_t skip_len = header.file_name_length + header.extra_field_length;
75 if( input.ignore(skip_len).eof() ) {
76 throw Error(_("SeekNextCod: EOF while skipping unused fields"));
79 return btohl(header.compressed_size);
81 else if( memcmp(signature, directory_sig, sizeof(signature)) == 0 ) {
82 // done reading when central directory is reached
83 return 0;
86 else if( memcmp(signature, codtype_simple, sizeof(codtype_simple)) == 0 ) {
87 // find and return size of cod file
88 if( input.seekg(0, ios::end).fail() ) {
89 throw Error(_("SeekNextCod: seek to end failed"));
92 uint32_t size = input.tellg();
94 if( input.seekg(0, ios::beg).fail() ) {
95 throw Error(_("SeekNextCod: seek to start failed"));
98 return size;
100 else {
101 throw Error(_("SeekNextCod: unknown COD file signature"));
104 return 0;
108 CodFileBuilder::CodFileBuilder(const std::string &module_name, size_t module_count)
109 : m_module_name(module_name)
110 , m_module_count(module_count)
111 , m_current_module(0)
115 CodFileBuilder::~CodFileBuilder()
119 void CodFileBuilder::WriteNextHeader(std::ostream &output, const uint8_t* module_buffer, uint32_t module_size)
121 // ignored for single module .cod files (simple .cod file)
122 if( m_module_count == 1 ) {
123 return;
126 // 32bit CRC of module in file header and directory entry
127 // using zero for CRC will result in warnings when inflating .cod file
128 uint32_t crc = 0;
130 #ifdef HAVE_ZLIB
131 crc = crc32(0, NULL, module_size);
132 crc = crc32(crc, module_buffer, module_size);
133 #endif
135 // .cod file name for siblings have hyphenated index number, name-1.cod
136 std::ostringstream file_name(m_module_name, ios::app);
137 if( m_current_module == 0 )
138 file_name << ".cod";
139 else
140 file_name << "-" << m_current_module << ".cod";
142 // current stream pointer is relative offset to start of file entry
143 uint32_t entry_offset = output.tellp();
145 // structures for the local file entry and central directory entry
146 pkzip_local_header_t header;
147 pkzip_directory_t entry;
149 // zero both structs, most fields are zero
150 memset(&header, 0, sizeof(pkzip_local_header_t));
151 memset(&entry, 0, sizeof(pkzip_directory_t));
153 char header_sig[] = PKZIP_LOCAL_FILE_SIG;
154 output.write(header_sig, sizeof(header_sig));
156 // version is always 0x00A0 = 'Windows NTFS'
157 header.version_needed = htobs(10);
159 // time and date fields seem to randomly have invalid or fixed values
160 // just leave them as zero
161 //header.last_mod_time
162 //header.last_mod_date
164 header.crc_32 = htobl(crc);
165 header.compressed_size = htobl(module_size);
166 header.uncompressed_size = htobl(module_size);
167 header.file_name_length = htobs(file_name.str().length());
169 // the very first cod sibling to be written has an extra field
170 // length equal to 4, with all zeros in the field itself
171 // all subsequent siblings have a zero length extra field
172 //header.extra_field_length = htobs(4);
174 output.write((char *)&header, sizeof(pkzip_local_header_t));
175 output << file_name.str();
177 char footer_sig[] = PKZIP_DIRECTORY_SIG;
179 // version is always 0x00A0 = 'Windows NTFS'
180 entry.version_madeby = htobs(10);
181 entry.version_needed = htobs(10);
183 entry.crc_32 = htobl(crc);
184 entry.compressed_size = htobl(module_size);
185 entry.uncompressed_size = htobl(module_size);
186 entry.file_name_length = htobs(file_name.str().length());
187 entry.relative_offset = htobl(entry_offset);
189 m_directory.write(footer_sig, sizeof(footer_sig));
190 m_directory.write((char*)&entry, sizeof(pkzip_directory_t));
191 m_directory << file_name.str();
193 m_current_module ++;
196 void CodFileBuilder::WriteFooter(std::ostream &output)
198 // ignored for single module .cod files (simple .cod file)
199 if( m_module_count == 1 ) {
200 return;
203 pkzip_end_directory_t end;
204 memset(&end, 0, sizeof(pkzip_end_directory_t));
206 end.this_disk_entry_count = htobs(m_current_module);
207 end.total_entry_count = htobs(m_current_module);
208 end.directory_length = htobl(m_directory.str().length());
210 // current stream pointer is relative offset to start of directory
211 end.directory_offset = output.tellp();
213 char sig[] = PKZIP_END_DIRECTORY_SIG;
215 output.write(m_directory.str().data(), m_directory.str().length());
216 output.write(sig, sizeof(sig));
217 output.write((char *)&end, sizeof(pkzip_end_directory_t));
221 } // namespace Barry