Added 'addmemo' example, to test newline behaviour
[barry.git] / src / cod.cc
blob44f0e00b456f992bfe39d289718e4beaf5fd5171
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
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 "cod.h"
25 #include "cod-internal.h"
26 #include "error.h"
27 #include "endian.h"
28 #include <config.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <string>
33 #include <string.h>
35 #ifdef HAVE_ZLIB
36 #include <zlib.h>
37 #endif
39 using namespace std;
41 namespace Barry {
44 size_t SeekNextCod(std::istream &input)
46 unsigned char codtype_simple[] = CODFILE_TYPE_SIMPLE;
47 char codtype_pkzip[] = CODFILE_TYPE_PKZIP;
48 char local_file_sig[] = PKZIP_LOCAL_FILE_SIG;
49 char directory_sig[] = PKZIP_DIRECTORY_SIG;
51 char signature[4];
53 // finished reading stream containing single cod file
54 input.peek();
55 if( input.eof() ) {
56 return 0;
59 if( input.read(signature, sizeof(signature)).eof() ) {
60 throw Error("SeekNextCod: EOF while reading file signature");
63 if( memcmp(signature, codtype_pkzip, sizeof(codtype_pkzip)) == 0 ) {
65 if( memcmp(signature, local_file_sig, sizeof(signature)) == 0 ) {
66 pkzip_local_header_t header;
68 if( input.read((char *)&header, sizeof(pkzip_local_header_t)).eof() ) {
69 throw Error("SeekNextCod: EOF while reading PKZIP header");
72 // skip cod file name and extra field, we don't need them
73 size_t skip_len = header.file_name_length + header.extra_field_length;
74 if( input.ignore(skip_len).eof() ) {
75 throw Error("SeekNextCod: EOF while skipping unused fields");
78 return btohl(header.compressed_size);
80 else if( memcmp(signature, directory_sig, sizeof(signature)) == 0 ) {
81 // done reading when central directory is reached
82 return 0;
85 else if( memcmp(signature, codtype_simple, sizeof(codtype_simple)) == 0 ) {
86 // find and return size of cod file
87 if( input.seekg(0, ios::end).fail() ) {
88 throw Error("SeekNextCod: seek to end failed");
91 uint32_t size = input.tellg();
93 if( input.seekg(0, ios::beg).fail() ) {
94 throw Error("SeekNextCod: seek to start failed");
97 return size;
99 else {
100 throw Error("SeekNextCod: unknown COD file signature");
103 return 0;
107 CodFileBuilder::CodFileBuilder(const std::string &module_name, size_t module_count)
108 : m_module_name(module_name)
109 , m_module_count(module_count)
110 , m_current_module(0)
114 CodFileBuilder::~CodFileBuilder()
118 void CodFileBuilder::WriteNextHeader(std::ostream &output, const uint8_t* module_buffer, uint32_t module_size)
120 // ignored for single module .cod files (simple .cod file)
121 if( m_module_count == 1 ) {
122 return;
125 // 32bit CRC of module in file header and directory entry
126 // using zero for CRC will result in warnings when inflating .cod file
127 uint32_t crc = 0;
129 #ifdef HAVE_ZLIB
130 crc = crc32(0, NULL, module_size);
131 crc = crc32(crc, module_buffer, module_size);
132 #endif
134 // .cod file name for siblings have hyphenated index number, name-1.cod
135 std::ostringstream file_name(m_module_name, ios::app);
136 if( m_current_module == 0 )
137 file_name << ".cod";
138 else
139 file_name << "-" << m_current_module << ".cod";
141 // current stream pointer is relative offset to start of file entry
142 uint32_t entry_offset = output.tellp();
144 // structures for the local file entry and central directory entry
145 pkzip_local_header_t header;
146 pkzip_directory_t entry;
148 // zero both structs, most fields are zero
149 memset(&header, 0, sizeof(pkzip_local_header_t));
150 memset(&entry, 0, sizeof(pkzip_directory_t));
152 char header_sig[] = PKZIP_LOCAL_FILE_SIG;
153 output.write(header_sig, sizeof(header_sig));
155 // version is always 0x00A0 = 'Windows NTFS'
156 header.version_needed = htobs(10);
158 // time and date fields seem to randomly have invalid or fixed values
159 // just leave them as zero
160 //header.last_mod_time
161 //header.last_mod_date
163 header.crc_32 = htobl(crc);
164 header.compressed_size = htobl(module_size);
165 header.uncompressed_size = htobl(module_size);
166 header.file_name_length = htobs(file_name.str().length());
168 // the very first cod sibling to be written has an extra field
169 // length equal to 4, with all zeros in the field itself
170 // all subsequent siblings have a zero length extra field
171 //header.extra_field_length = htobs(4);
173 output.write((char *)&header, sizeof(pkzip_local_header_t));
174 output << file_name.str();
176 char footer_sig[] = PKZIP_DIRECTORY_SIG;
178 // version is always 0x00A0 = 'Windows NTFS'
179 entry.version_madeby = htobs(10);
180 entry.version_needed = htobs(10);
182 entry.crc_32 = htobl(crc);
183 entry.compressed_size = htobl(module_size);
184 entry.uncompressed_size = htobl(module_size);
185 entry.file_name_length = htobs(file_name.str().length());
186 entry.relative_offset = htobl(entry_offset);
188 m_directory.write(footer_sig, sizeof(footer_sig));
189 m_directory.write((char*)&entry, sizeof(pkzip_directory_t));
190 m_directory << file_name.str();
192 m_current_module ++;
195 void CodFileBuilder::WriteFooter(std::ostream &output)
197 // ignored for single module .cod files (simple .cod file)
198 if( m_module_count == 1 ) {
199 return;
202 pkzip_end_directory_t end;
203 memset(&end, 0, sizeof(pkzip_end_directory_t));
205 end.this_disk_entry_count = htobs(m_current_module);
206 end.total_entry_count = htobs(m_current_module);
207 end.directory_length = htobl(m_directory.str().length());
209 // current stream pointer is relative offset to start of directory
210 end.directory_offset = output.tellp();
212 char sig[] = PKZIP_END_DIRECTORY_SIG;
214 output.write(m_directory.str().data(), m_directory.str().length());
215 output.write(sig, sizeof(sig));
216 output.write((char *)&end, sizeof(pkzip_end_directory_t));
220 } // namespace Barry