DOSBox-X
|
00001 00002 #ifndef _GNU_SOURCE 00003 # define _GNU_SOURCE 00004 #endif 00005 00006 #include <stdint.h> 00007 #include <stdlib.h> 00008 #include <string.h> 00009 #include <stdio.h> 00010 #include <unistd.h> 00011 #include <assert.h> 00012 #include <stdarg.h> 00013 #include <fcntl.h> 00014 #include <sys/types.h> 00015 #include <algorithm> // std::transform 00016 #ifdef WIN32 00017 # include <signal.h> 00018 # include <sys/stat.h> 00019 # include <process.h> 00020 #endif 00021 00022 #include "dosbox.h" 00023 #include "pic.h" 00024 #include "timer.h" 00025 #include "setup.h" 00026 #include "bios.h" 00027 #include "support.h" 00028 #include "debug.h" 00029 #include "ide.h" 00030 #include "bitop.h" 00031 #include "ptrop.h" 00032 #include "mapper.h" 00033 #include "zipfile.h" 00034 00035 #include "mapper.h" 00036 #include "vga.h" 00037 #include "keyboard.h" 00038 #include "cpu.h" 00039 #include "fpu.h" 00040 #include "cross.h" 00041 #include "keymap.h" 00042 00043 static std::string zip_nv_pair_empty; 00044 00045 static char zip_nv_tmp[1024]; 00046 00047 bool ZIPFileEntry::rewind(void) { 00048 if (can_write) return false; 00049 return (seek_file(0) == 0); 00050 } 00051 00052 off_t ZIPFileEntry::seek_file(off_t pos) { 00053 if (file == NULL || file_offset == (off_t)0 || can_write) return (off_t)(-1LL); 00054 if (pos < (off_t)0) pos = (off_t)0; 00055 if (pos > file_length) pos = file_length; 00056 pos = file->seek_file(pos + file_offset) - file_offset; 00057 if (pos < 0 || pos > file_length) return (off_t)(-1LL); 00058 position = pos; 00059 return pos; 00060 } 00061 00062 int ZIPFileEntry::read(void *buffer,size_t count) { 00063 if (file == NULL || file_offset == (off_t)0) return -1; 00064 if (position >= file_length) return 0; 00065 00066 size_t mread = (size_t)file_length - (size_t)position; 00067 if (mread > count) mread = count; 00068 00069 if (mread > 0) { 00070 if (seek_file(position) != position) return -1; 00071 int r = file->read(buffer,mread); 00072 if (r > 0) position += (off_t)r; 00073 return r; 00074 } 00075 00076 return (int)mread; 00077 } 00078 00079 int ZIPFileEntry::write(const void *buffer,size_t count) { 00080 if (file == NULL || file_offset == (off_t)0 || !can_write) return -1; 00081 00082 /* write stream only, no seeking. 00083 * this code assumes the file pointer will not change anywhere else, 00084 * and always to the end */ 00085 if (count > size_t(0)) { 00086 int r = file->write(buffer,count); 00087 if (r > 0) { 00088 position += (off_t)r; 00089 write_crc = zipcrc_update(write_crc, buffer, size_t(r)); 00090 file_length = position; 00091 } 00092 return r; 00093 } 00094 00095 return (int)count; 00096 } 00097 00098 ZIPFile::ZIPFile() { 00099 } 00100 00101 ZIPFile::~ZIPFile() { 00102 close(); 00103 } 00104 00105 void ZIPFile::close(void) { 00106 if (file_fd >= 0) { 00107 ::close(file_fd); 00108 file_fd = -1; 00109 } 00110 00111 entries.clear(); 00112 } 00113 00114 ZIPFileEntry *ZIPFile::get_entry(const char *name) { 00115 if (file_fd < 0) return NULL; 00116 00117 /* no reading while writing except what is being written */ 00118 if (!current_entry.empty() && current_entry != name) return NULL; 00119 00120 /* no empty names */ 00121 if (*name == 0) return NULL; 00122 00123 auto i = entries.find(name); 00124 if (i == entries.end()) return NULL; 00125 00126 return &(i->second); 00127 } 00128 00129 ZIPFileEntry *ZIPFile::new_entry(const char *name) { 00130 if (file_fd < 0 || !can_write || wrote_trailer) return NULL; 00131 00132 /* cannot make new entries that exist already */ 00133 auto i = entries.find(name); 00134 if (i != entries.end()) return NULL; 00135 00136 /* no empty names */ 00137 if (*name == 0) return NULL; 00138 00139 /* close current entry, if open */ 00140 close_current(); 00141 00142 /* begin new entry at end */ 00143 current_entry = name; 00144 write_pos = end_of_file(); 00145 00146 ZIPFileEntry *ent = &entries[name]; 00147 ent->name = name; 00148 ent->can_write = true; 00149 ent->file_header_offset = write_pos; 00150 write_pos += (off_t)(sizeof(ZIPLocalFileHeader) + ent->name.length()); 00151 ent->write_crc = zipcrc_init(); 00152 ent->file_offset = write_pos; 00153 ent->file = this; 00154 00155 if (seek_file(ent->file_header_offset) != ent->file_header_offset) { 00156 close_current(); 00157 return NULL; 00158 } 00159 00160 ZIPLocalFileHeader hdr; 00161 memset(&hdr,0,sizeof(hdr)); 00162 hdr.local_file_header_signature = htole32(0x04034b50); /* PK\x03\x04 */ 00163 hdr.version_needed_to_extract = htole16(20); /* PKZIP 2.0 */ 00164 hdr.general_purpose_bit_flag = htole16(0 << 1); 00165 hdr.compression_method = 0; /* store (no compression) */ 00166 hdr.file_name_length = htole16((uint16_t)ent->name.length()); 00167 if (write(&hdr,sizeof(hdr)) != sizeof(hdr)) { 00168 close_current(); 00169 return NULL; 00170 } 00171 assert(ent->name.length() != 0); 00172 if ((size_t)write(ent->name.c_str(),ent->name.length()) != ent->name.length()) { 00173 close_current(); 00174 return NULL; 00175 } 00176 if (seek_file(ent->file_offset) != ent->file_offset) { 00177 close_current(); 00178 return NULL; 00179 } 00180 00181 return ent; 00182 } 00183 00184 off_t ZIPFile::end_of_file(void) { 00185 return lseek(file_fd,0,SEEK_END); 00186 } 00187 00188 void ZIPFile::close_current(void) { 00189 if (!can_write) return; 00190 00191 if (!current_entry.empty()) { 00192 ZIPFileEntry *ent = get_entry(current_entry.c_str()); 00193 ZIPLocalFileHeader hdr; 00194 00195 if (ent != NULL && ent->can_write) { 00196 ent->can_write = false; 00197 00198 if (seek_file(ent->file_header_offset) == ent->file_header_offset && read(&hdr,sizeof(hdr)) == sizeof(hdr)) { 00199 hdr.compressed_size = hdr.uncompressed_size = htole32(((uint32_t)ent->file_length)); 00200 hdr.crc_32 = htole32(zipcrc_finalize(ent->write_crc)); 00201 00202 if (seek_file(ent->file_header_offset) == ent->file_header_offset && write(&hdr,sizeof(hdr)) == sizeof(hdr)) { 00203 /* good */ 00204 } 00205 } 00206 } 00207 } 00208 00209 current_entry.clear(); 00210 } 00211 00212 int ZIPFile::open(const char *path,int mode) { 00213 close(); 00214 00215 if (path == NULL) return -1; 00216 00217 if ((mode & 3) == O_WRONLY) { 00218 LOG_MSG("WARNING: ZIPFile attempt to open with O_WRONLY, which will not work"); 00219 return -1; 00220 } 00221 00222 #if defined(O_BINARY) 00223 mode |= O_BINARY; 00224 #endif 00225 00226 file_fd = ::open(path,mode,0644); 00227 if (file_fd < 0) return -1; 00228 if (lseek(file_fd,0,SEEK_SET) != 0) { 00229 close(); 00230 return -1; 00231 } 00232 00233 entries.clear(); 00234 current_entry.clear(); 00235 wrote_trailer = false; 00236 write_pos = 0; 00237 00238 /* WARNING: This assumes O_RDONLY, O_WRONLY, O_RDWR are defined as in Linux (0, 1, 2) in the low two bits */ 00239 if ((mode & 3) == O_RDWR) 00240 can_write = true; 00241 else 00242 can_write = false; 00243 00244 /* if we're supposed to READ the ZIP file, then start scanning now */ 00245 if ((mode & 3) == O_RDONLY) { 00246 unsigned char tmp[512]; 00247 struct pkzip_central_directory_header_main chdr; 00248 struct pkzip_central_directory_header_end ehdr; 00249 00250 off_t fsz = end_of_file(); 00251 00252 /* check for 'PK' at the start of the file. 00253 * This code only expects to handle the ZIP files it generated, not ZIP files in general. */ 00254 if (fsz < 64 || seek_file(0) != 0 || read(tmp,4) != 4 || memcmp(tmp,"PK\x03\x04",4) != 0) { 00255 LOG_MSG("Not a PKZIP file"); 00256 close(); 00257 return -1; 00258 } 00259 00260 /* then look for the central directory at the end. 00261 * this code DOES NOT SUPPORT the ZIP comment field, nor will this code generate one. */ 00262 if (seek_file(fsz - (off_t)sizeof(ehdr)) != (fsz - (off_t)sizeof(ehdr)) || (size_t)read(&ehdr,sizeof(ehdr)) != sizeof(ehdr) || ehdr.sig != PKZIP_CENTRAL_DIRECTORY_END_SIG || ehdr.size_of_central_directory > 0x100000u/*absurd size*/ || ehdr.offset_of_central_directory_from_start_disk == 0 || (off_t)ehdr.offset_of_central_directory_from_start_disk >= fsz) { 00263 LOG_MSG("Cannot locate Central Directory"); 00264 close(); 00265 return -1; 00266 } 00267 if (seek_file((off_t)ehdr.offset_of_central_directory_from_start_disk) != (off_t)ehdr.offset_of_central_directory_from_start_disk) { 00268 LOG_MSG("Cannot locate Central Directory #2"); 00269 close(); 00270 return -1; 00271 } 00272 00273 /* read the central directory */ 00274 { 00275 long remain = (long)ehdr.size_of_central_directory; 00276 00277 while (remain >= (long)sizeof(struct pkzip_central_directory_header_main)) { 00278 if (read(&chdr,sizeof(chdr)) != sizeof(chdr)) break; 00279 remain -= (long)sizeof(chdr); 00280 00281 if (chdr.sig != PKZIP_CENTRAL_DIRECTORY_HEADER_SIG) break; 00282 if (chdr.filename_length >= sizeof(tmp)) break; 00283 00284 tmp[chdr.filename_length] = 0; 00285 if (chdr.filename_length != 0) { 00286 if (read(tmp,chdr.filename_length) != chdr.filename_length) break; 00287 remain -= chdr.filename_length; 00288 } 00289 00290 if (tmp[0] == 0) continue; 00291 00292 ZIPFileEntry *ent = &entries[(char*)tmp]; 00293 ent->can_write = false; 00294 ent->file_length = (off_t)htole32(chdr.uncompressed_size); 00295 ent->file_header_offset = (off_t)htole32(chdr.relative_offset_of_local_header); 00296 ent->file_offset = ent->file_header_offset + (off_t)sizeof(struct ZIPLocalFileHeader) + (off_t)htole16(chdr.filename_length) + (off_t)htole16(chdr.extra_field_length); 00297 ent->position = 0; 00298 ent->name = (char*)tmp; 00299 ent->file = this; 00300 } 00301 } 00302 } 00303 00304 return 0; 00305 } 00306 00307 off_t ZIPFile::seek_file(off_t pos) { 00308 if (file_fd < 0) return (off_t)(-1LL); 00309 return ::lseek(file_fd,pos,SEEK_SET); 00310 } 00311 00312 int ZIPFile::read(void *buffer,size_t count) { 00313 if (file_fd < 0) return -1; 00314 return ::read(file_fd,buffer,(unsigned int)count); 00315 } 00316 00317 int ZIPFile::write(const void *buffer,size_t count) { 00318 if (file_fd < 0) return -1; 00319 return ::write(file_fd,buffer,(unsigned int)count); 00320 } 00321 00322 void ZIPFile::writeZIPFooter(void) { 00323 struct pkzip_central_directory_header_main chdr; 00324 struct pkzip_central_directory_header_end ehdr; 00325 uint32_t cdircount = 0; 00326 uint32_t cdirbytes = 0; 00327 off_t cdirofs = 0; 00328 00329 if (file_fd < 0 || wrote_trailer || !can_write) return; 00330 00331 close_current(); 00332 cdirofs = end_of_file(); 00333 00334 for (auto i=entries.begin();i!=entries.end();i++) { 00335 const ZIPFileEntry &ent = i->second; 00336 00337 memset(&chdr,0,sizeof(chdr)); 00338 chdr.sig = htole32(PKZIP_CENTRAL_DIRECTORY_HEADER_SIG); 00339 chdr.version_made_by = htole16((0 << 8) + 20); /* PKZIP 2.0 */ 00340 chdr.version_needed_to_extract = htole16(20); /* PKZIP 2.0 or higher */ 00341 chdr.general_purpose_bit_flag = htole16(0 << 1); /* just lie and say that "normal" deflate was used */ 00342 chdr.compression_method = 0; /* stored (no compression) */ 00343 chdr.last_mod_file_time = 0; 00344 chdr.last_mod_file_date = 0; 00345 chdr.compressed_size = htole32(((uint32_t)ent.file_length)); 00346 chdr.uncompressed_size = htole32(((uint32_t)ent.file_length)); 00347 chdr.filename_length = htole16((uint16_t)ent.name.length()); 00348 chdr.disk_number_start = htole16(1u); 00349 chdr.internal_file_attributes = 0; 00350 chdr.external_file_attributes = 0; 00351 chdr.relative_offset_of_local_header = (uint32_t)htole32(ent.file_header_offset); 00352 chdr.crc32 = htole32(zipcrc_finalize(ent.write_crc)); 00353 00354 if (write(&chdr,sizeof(chdr)) != sizeof(chdr)) break; 00355 cdirbytes += sizeof(chdr); 00356 cdircount++; 00357 00358 assert(ent.name.length() != 0); 00359 if ((size_t)write(ent.name.c_str(),ent.name.length()) != ent.name.length()) break; 00360 cdirbytes += (uint32_t)ent.name.length(); 00361 } 00362 00363 memset(&ehdr,0,sizeof(ehdr)); 00364 ehdr.sig = htole32(PKZIP_CENTRAL_DIRECTORY_END_SIG); 00365 ehdr.number_of_disk_with_start_of_central_directory = htole16(0); 00366 ehdr.number_of_this_disk = htole16(0); 00367 ehdr.total_number_of_entries_of_central_dir_on_this_disk = htole16(cdircount); 00368 ehdr.total_number_of_entries_of_central_dir = htole16(cdircount); 00369 ehdr.size_of_central_directory = htole32(cdirbytes); 00370 ehdr.offset_of_central_directory_from_start_disk = (uint32_t)htole32(cdirofs); 00371 write(&ehdr,sizeof(ehdr)); 00372 00373 wrote_trailer = true; 00374 current_entry.clear(); 00375 } 00376 00377 zip_nv_pair_map::zip_nv_pair_map() { 00378 } 00379 00380 zip_nv_pair_map::zip_nv_pair_map(ZIPFileEntry &ent) { 00381 read_nv_pairs(ent); 00382 } 00383 00384 std::string &zip_nv_pair_map::get(const char *name) { 00385 auto i = find(name); 00386 if (i != end()) return i->second; 00387 return zip_nv_pair_empty; 00388 } 00389 00390 bool zip_nv_pair_map::get_bool(const char *name) { 00391 std::string &val = get(name); 00392 return (strtol(val.c_str(),NULL,0) > 0); 00393 } 00394 00395 long zip_nv_pair_map::get_long(const char *name) { 00396 std::string &val = get(name); 00397 return strtol(val.c_str(),NULL,0); 00398 } 00399 00400 unsigned long zip_nv_pair_map::get_ulong(const char *name) { 00401 std::string &val = get(name); 00402 return strtoul(val.c_str(),NULL,0); 00403 } 00404 00405 void zip_nv_pair_map::process_line(char *line/*will modify, assume caller has put NUL at the end*/) { 00406 char *equ = strchr(line,'='); 00407 if (equ == NULL) return; 00408 *equ++ = 0; /* overwite '=' with NUL, split name vs value */ 00409 00410 /* no null names */ 00411 if (*line == 0) return; 00412 00413 (*this)[line] = equ; 00414 } 00415 00416 void zip_nv_pair_map::read_nv_pairs(ZIPFileEntry &ent) { 00417 char tmp[1024]; 00418 char line[1024],*w,*wf=line+sizeof(line)-1; 00419 char c; 00420 int l; 00421 00422 clear(); 00423 ent.rewind(); 00424 00425 w = line; 00426 while ((l=ent.read(tmp,sizeof(tmp))) > 0) { 00427 char* r = tmp; 00428 char* f = tmp + l; 00429 00430 while (r < f) { 00431 c = *r++; 00432 00433 if (c == '\n') { 00434 assert(w <= wf); 00435 *w = 0; 00436 process_line(line); 00437 w = line; 00438 } 00439 else if (c == '\r') { 00440 /* ignore */ 00441 } 00442 else if (w < wf) { 00443 *w++ = c; 00444 } 00445 } 00446 } 00447 00448 if (w != line) { 00449 assert(w <= wf); 00450 *w = 0; 00451 process_line(line); 00452 w = line; 00453 } 00454 } 00455 00456 void zip_nv_write(ZIPFileEntry &ent,const char *name,bool val) { 00457 size_t l; 00458 00459 if ((l = ((size_t)snprintf(zip_nv_tmp,sizeof(zip_nv_tmp),"%s=%d\n",name,val?1:0))) >= (sizeof(zip_nv_tmp)-1u)) 00460 E_Exit("zip_nv_write buffer overrun (result too long)"); 00461 00462 ent.write(zip_nv_tmp,l); 00463 } 00464 00465 void zip_nv_write(ZIPFileEntry &ent,const char *name,long val) { 00466 size_t l; 00467 00468 if ((l = ((size_t)snprintf(zip_nv_tmp,sizeof(zip_nv_tmp),"%s=%ld\n",name,val))) >= (sizeof(zip_nv_tmp)-1u)) 00469 E_Exit("zip_nv_write buffer overrun (result too long)"); 00470 00471 ent.write(zip_nv_tmp,l); 00472 } 00473 00474 void zip_nv_write_hex(ZIPFileEntry &ent,const char *name,unsigned long val) { 00475 size_t l; 00476 00477 if ((l = ((size_t)snprintf(zip_nv_tmp,sizeof(zip_nv_tmp),"%s=0x%lx\n",name,val))) >= (sizeof(zip_nv_tmp)-1u)) 00478 E_Exit("zip_nv_write buffer overrun (result too long)"); 00479 00480 ent.write(zip_nv_tmp,l); 00481 } 00482