| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header$"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | rcmain.cpp - T3 resource compiler main |
| | 15 | Function |
| | 16 | |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 01/03/00 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | |
| | 24 | #include <stdlib.h> |
| | 25 | #include <string.h> |
| | 26 | #include <stdio.h> |
| | 27 | #include <ctype.h> |
| | 28 | #include <time.h> |
| | 29 | |
| | 30 | #include "os.h" |
| | 31 | #include "t3std.h" |
| | 32 | #include "rcmain.h" |
| | 33 | #include "vmimage.h" |
| | 34 | |
| | 35 | |
| | 36 | /* |
| | 37 | * copy a block of bytes from the input file to the output file |
| | 38 | */ |
| | 39 | static int copy_file_bytes(osfildef *fpin, osfildef *fpout, ulong siz) |
| | 40 | { |
| | 41 | static char copybuf[16 * 1024]; |
| | 42 | size_t cursiz; |
| | 43 | |
| | 44 | /* copy bytes until we run out */ |
| | 45 | while (siz != 0) |
| | 46 | { |
| | 47 | /* we can copy up to one full buffer at a time */ |
| | 48 | cursiz = (siz > sizeof(copybuf) ? sizeof(copybuf) : (size_t)siz); |
| | 49 | |
| | 50 | /* deduct the amount we're copying from the total */ |
| | 51 | siz -= cursiz; |
| | 52 | |
| | 53 | /* read from input, copy to output */ |
| | 54 | if (osfrb(fpin, copybuf, cursiz) |
| | 55 | || osfwb(fpout, copybuf, cursiz)) |
| | 56 | return 1; |
| | 57 | } |
| | 58 | |
| | 59 | /* success */ |
| | 60 | return 0; |
| | 61 | } |
| | 62 | |
| | 63 | /* |
| | 64 | * Add resources |
| | 65 | */ |
| | 66 | int CResCompMain::add_resources(const char *image_fname, |
| | 67 | const class CRcResList *reslist, |
| | 68 | class CRcHostIfc *hostifc, |
| | 69 | int create_new, os_filetype_t file_type, |
| | 70 | int link_mode) |
| | 71 | { |
| | 72 | osfildef *fp = 0; |
| | 73 | osfildef *resfp = 0; |
| | 74 | char buf[256 + 256 + 128]; |
| | 75 | long mres_seek; |
| | 76 | long mres_size; |
| | 77 | CRcResEntry *entry; |
| | 78 | long ofs; |
| | 79 | long contents_siz; |
| | 80 | |
| | 81 | /* |
| | 82 | * if the file doesn't exist, and we're not creating a new file, |
| | 83 | * it's an error |
| | 84 | */ |
| | 85 | if (osfacc(image_fname) && !create_new) |
| | 86 | { |
| | 87 | /* we can't create a new file - display an error and give up */ |
| | 88 | disp_error(hostifc, "image file \"%.*s\" does not exist", |
| | 89 | (int)OSFNMAX, image_fname); |
| | 90 | goto ret_error; |
| | 91 | } |
| | 92 | |
| | 93 | /* if we're creating a new file, write the header */ |
| | 94 | if (create_new) |
| | 95 | { |
| | 96 | time_t timer; |
| | 97 | struct tm *tblock; |
| | 98 | |
| | 99 | /* create a new image file */ |
| | 100 | fp = osfopwb(image_fname, file_type); |
| | 101 | if (fp == 0) |
| | 102 | { |
| | 103 | disp_error(hostifc, "can't create file \"%.*s\"", |
| | 104 | (int)OSFNMAX, image_fname); |
| | 105 | goto ret_error; |
| | 106 | } |
| | 107 | |
| | 108 | /* set the version ID in the header */ |
| | 109 | oswp2(buf, 1); |
| | 110 | |
| | 111 | /* set the reserved bytes in the header */ |
| | 112 | memset(buf + 2, 0, 32); |
| | 113 | |
| | 114 | /* set the timestamp in the header */ |
| | 115 | timer = time(NULL); |
| | 116 | tblock = localtime(&timer); |
| | 117 | memcpy(buf + 2 + 32, asctime(tblock), 24); |
| | 118 | |
| | 119 | /* set up an EOF block */ |
| | 120 | memcpy(buf + 2 + 32 + 24, "EOF ", 4); |
| | 121 | memset(buf + 2 + 32 + 24 + 4, 0, 4); |
| | 122 | oswp2(buf + 2 + 32 + 24 + 4 + 4, VMIMAGE_DBF_MANDATORY); |
| | 123 | |
| | 124 | /* write the header and the EOF block */ |
| | 125 | if (osfwb(fp, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1) |
| | 126 | || osfwb(fp, buf, 2 + 32 + 24 + 10)) |
| | 127 | { |
| | 128 | disp_error(hostifc, "%.*s: error writing new file header", |
| | 129 | (int)OSFNMAX, image_fname); |
| | 130 | goto ret_error; |
| | 131 | } |
| | 132 | |
| | 133 | /* done with the file - close it for now */ |
| | 134 | osfcls(fp); |
| | 135 | fp = 0; |
| | 136 | } |
| | 137 | |
| | 138 | /* |
| | 139 | * open the file for reading and writing, since we'll need to read |
| | 140 | * through the current contents then write our new data after the |
| | 141 | * end of the existing file |
| | 142 | */ |
| | 143 | fp = osfoprwb(image_fname, file_type); |
| | 144 | if (fp == 0) |
| | 145 | { |
| | 146 | /* display an error and give up */ |
| | 147 | disp_error(hostifc, "can't open file \"%.*s\"", |
| | 148 | (int)OSFNMAX, image_fname); |
| | 149 | goto ret_error; |
| | 150 | } |
| | 151 | |
| | 152 | /* read and verify the header */ |
| | 153 | if (osfrb(fp, buf, sizeof(VMIMAGE_SIG)-1 + 2 + 32 + 24) |
| | 154 | || memcmp(buf, VMIMAGE_SIG, sizeof(VMIMAGE_SIG)-1) != 0) |
| | 155 | { |
| | 156 | disp_error(hostifc, "%.*s: invalid image file header", |
| | 157 | (int)OSFNMAX, image_fname); |
| | 158 | goto ret_error; |
| | 159 | } |
| | 160 | |
| | 161 | /* read and skip the blocks until we reach the "EOF" block */ |
| | 162 | for (;;) |
| | 163 | { |
| | 164 | long block_siz; |
| | 165 | |
| | 166 | /* read the next block */ |
| | 167 | if (osfrb(fp, buf, 10)) |
| | 168 | { |
| | 169 | disp_error(hostifc, "%.*s: unexpected end of file", |
| | 170 | (int)OSFNMAX, image_fname); |
| | 171 | goto ret_error; |
| | 172 | } |
| | 173 | |
| | 174 | /* if it's EOF, we're done */ |
| | 175 | if (memcmp(buf, "EOF ", 4) == 0) |
| | 176 | break; |
| | 177 | |
| | 178 | /* read the size of this block */ |
| | 179 | block_siz = t3rp4u(buf + 4); |
| | 180 | |
| | 181 | /* skip past this block */ |
| | 182 | osfseek(fp, block_siz, OSFSK_CUR); |
| | 183 | } |
| | 184 | |
| | 185 | /* |
| | 186 | * we've found the EOF block - make sure we're at the end of the |
| | 187 | * file |
| | 188 | */ |
| | 189 | mres_seek = osfpos(fp); |
| | 190 | osfseek(fp, 0, OSFSK_END); |
| | 191 | if (mres_seek != osfpos(fp)) |
| | 192 | { |
| | 193 | disp_error(hostifc, "%.*s: extra data after end of file", |
| | 194 | (int)OSFNMAX, image_fname); |
| | 195 | goto ret_error; |
| | 196 | } |
| | 197 | |
| | 198 | /* |
| | 199 | * seek back to the start of the EOF block, so that we can overwrite |
| | 200 | * it with a new MRES block |
| | 201 | */ |
| | 202 | mres_seek -= 10; |
| | 203 | osfseek(fp, mres_seek, OSFSK_SET); |
| | 204 | |
| | 205 | /* |
| | 206 | * Prepare and write the MRES block header, plus the resource entry |
| | 207 | * count. If we're in "link mode," write an MREL header instead. |
| | 208 | */ |
| | 209 | memcpy(buf, link_mode ? "MREL" : "MRES", 4); |
| | 210 | memset(buf + 4, 0, 6); |
| | 211 | oswp2(buf + 10, reslist->get_count()); |
| | 212 | if (osfwb(fp, buf, 10 + 2)) |
| | 213 | { |
| | 214 | disp_error(hostifc, "%.*s: error writing resource block header", |
| | 215 | (int)OSFNMAX, image_fname); |
| | 216 | goto ret_error; |
| | 217 | } |
| | 218 | |
| | 219 | /* |
| | 220 | * First, figure out how much space the table of contents itself |
| | 221 | * will take up. We need this information so we will know where the |
| | 222 | * first resource's binary data stream begins. Note that the |
| | 223 | * contents offset starts at 2, since the table entry count (a |
| | 224 | * UINT2) comes before the table's first entry. |
| | 225 | */ |
| | 226 | for (contents_siz = 2, entry = reslist->get_head() ; entry != 0 ; |
| | 227 | entry = entry->get_next()) |
| | 228 | { |
| | 229 | /* |
| | 230 | * each entry in the table of contents requires a UINT4 (offset |
| | 231 | * of the data), UINT4 (size of the data), UBYTE (name length), |
| | 232 | * and the bytes for the name itself |
| | 233 | */ |
| | 234 | contents_siz += 4 + 4 + 1 + strlen(entry->get_url()); |
| | 235 | } |
| | 236 | |
| | 237 | /* build the table of contents */ |
| | 238 | for (ofs = contents_siz, entry = reslist->get_head() ; entry != 0 ; |
| | 239 | entry = entry->get_next()) |
| | 240 | { |
| | 241 | long res_size; |
| | 242 | size_t url_len; |
| | 243 | char *p; |
| | 244 | size_t rem; |
| | 245 | |
| | 246 | /* if the entry name is too long, it's an error */ |
| | 247 | url_len = strlen(entry->get_url()); |
| | 248 | if (url_len > 255) |
| | 249 | { |
| | 250 | disp_error(hostifc, |
| | 251 | "%.*s: resource name \"%.*s\" for file \"%.*s\" " |
| | 252 | "is too long", |
| | 253 | (int)OSFNMAX, image_fname, |
| | 254 | (int)OSFNMAX, entry->get_url(), |
| | 255 | (int)OSFNMAX, entry->get_fname()); |
| | 256 | goto ret_error; |
| | 257 | } |
| | 258 | |
| | 259 | /* |
| | 260 | * if we're in "link mode", the table of contents entry consists of |
| | 261 | * simply the resource name plus the linked filename - we don't |
| | 262 | * care about the contents of the local file in this case |
| | 263 | */ |
| | 264 | if (link_mode) |
| | 265 | { |
| | 266 | size_t flen; |
| | 267 | |
| | 268 | /* make sure the local filename isn't too long, either */ |
| | 269 | flen = strlen(entry->get_fname()); |
| | 270 | if (flen > 255) |
| | 271 | { |
| | 272 | disp_error(hostifc, |
| | 273 | "%.*s: filename \"%.*s\" for resource link " |
| | 274 | "\"%.*s\" is too long", |
| | 275 | (int)OSFNMAX, image_fname, |
| | 276 | (int)OSFNMAX, entry->get_fname(), |
| | 277 | (int)OSFNMAX, entry->get_url()); |
| | 278 | goto ret_error; |
| | 279 | } |
| | 280 | |
| | 281 | /* |
| | 282 | * build the block: resource name len, resource name, filename |
| | 283 | * len, filenam |
| | 284 | */ |
| | 285 | buf[0] = (uchar)url_len; |
| | 286 | memcpy(buf + 1, entry->get_url(), url_len); |
| | 287 | buf[1 + url_len] = (uchar)flen; |
| | 288 | memcpy(buf + 1 + url_len + 1, entry->get_fname(), flen); |
| | 289 | |
| | 290 | /* write the block */ |
| | 291 | if (osfwb(fp, buf, 1 + url_len + 1 + flen)) |
| | 292 | { |
| | 293 | disp_error(hostifc, "%.*s: error writing contents entry for " |
| | 294 | "resource link \"%.*s\"", |
| | 295 | (int)OSFNMAX, image_fname, |
| | 296 | (int)OSFNMAX, entry->get_url()); |
| | 297 | goto ret_error; |
| | 298 | } |
| | 299 | |
| | 300 | /* that's all for a link mode entry */ |
| | 301 | continue; |
| | 302 | } |
| | 303 | |
| | 304 | /* open this resource file */ |
| | 305 | resfp = osfoprb(entry->get_fname(), OSFTBIN); |
| | 306 | if (resfp == 0) |
| | 307 | { |
| | 308 | disp_error(hostifc, "%.*s: cannot open resource file \"%.*s\"", |
| | 309 | (int)OSFNMAX, image_fname, |
| | 310 | (int)OSFNMAX, entry->get_fname()); |
| | 311 | goto ret_error; |
| | 312 | } |
| | 313 | |
| | 314 | /* |
| | 315 | * seek to the end of the resource file so we can determine its |
| | 316 | * size |
| | 317 | */ |
| | 318 | osfseek(resfp, 0, OSFSK_END); |
| | 319 | res_size = osfpos(resfp); |
| | 320 | |
| | 321 | /* build this table entry */ |
| | 322 | oswp4(buf, ofs); |
| | 323 | oswp4(buf + 4, res_size); |
| | 324 | buf[8] = (char)url_len; |
| | 325 | memcpy(buf + 9, entry->get_url(), url_len); |
| | 326 | |
| | 327 | /* mask the resource name by xor'ing each byte with 0xff */ |
| | 328 | for (p = buf + 9, rem = url_len ; rem != 0 ; --rem, ++p) |
| | 329 | *p ^= 0xFF; |
| | 330 | |
| | 331 | /* write the entry */ |
| | 332 | if (osfwb(fp, buf, 9 + url_len)) |
| | 333 | { |
| | 334 | disp_error(hostifc, "%.*s: error writing contents entry for " |
| | 335 | "resource \"%.*s\"", |
| | 336 | (int)OSFNMAX, image_fname, |
| | 337 | (int)OSFNMAX, entry->get_url()); |
| | 338 | goto ret_error; |
| | 339 | } |
| | 340 | |
| | 341 | /* add the resource's size into the offset so far */ |
| | 342 | ofs += res_size; |
| | 343 | |
| | 344 | /* we're done with this file for now */ |
| | 345 | osfcls(resfp); |
| | 346 | resfp = 0; |
| | 347 | } |
| | 348 | |
| | 349 | /* now copy the resources themselves, if we're not in link-only mode */ |
| | 350 | if (!link_mode) |
| | 351 | { |
| | 352 | /* run through our resource list */ |
| | 353 | for (entry = reslist->get_head() ; entry != 0 ; |
| | 354 | entry = entry->get_next()) |
| | 355 | { |
| | 356 | long res_size; |
| | 357 | char msg[OSFNMAX*2 + 20]; |
| | 358 | |
| | 359 | /* show what we're doing */ |
| | 360 | sprintf(msg, "+ %.*s (%.*s)", |
| | 361 | (int)OSFNMAX, entry->get_fname(), |
| | 362 | (int)OSFNMAX, entry->get_url()); |
| | 363 | hostifc->display_status(msg); |
| | 364 | |
| | 365 | /* open this resource file */ |
| | 366 | resfp = osfoprb(entry->get_fname(), OSFTBIN); |
| | 367 | if (resfp == 0) |
| | 368 | { |
| | 369 | disp_error(hostifc, |
| | 370 | "%.*s: cannot open resource file \"%.*s\"", |
| | 371 | (int)OSFNMAX, image_fname, |
| | 372 | (int)OSFNMAX, entry->get_fname()); |
| | 373 | goto ret_error; |
| | 374 | } |
| | 375 | |
| | 376 | /* get the size of the file */ |
| | 377 | osfseek(resfp, 0, OSFSK_END); |
| | 378 | res_size = osfpos(resfp); |
| | 379 | osfseek(resfp, 0, OSFSK_SET); |
| | 380 | |
| | 381 | /* copy the resource file's contents into the image file */ |
| | 382 | if (copy_file_bytes(resfp, fp, res_size)) |
| | 383 | { |
| | 384 | disp_error(hostifc, "%.*s: error copying resource file \"%.*s\"", |
| | 385 | (int)OSFNMAX, image_fname, |
| | 386 | (int)OSFNMAX, entry->get_fname()); |
| | 387 | goto ret_error; |
| | 388 | } |
| | 389 | |
| | 390 | /* we're done with this file for now */ |
| | 391 | osfcls(resfp); |
| | 392 | resfp = 0; |
| | 393 | } |
| | 394 | } |
| | 395 | |
| | 396 | /* |
| | 397 | * calculate the size of the MRES/MREL data (excluding the 10-byte |
| | 398 | * standard block header) |
| | 399 | */ |
| | 400 | mres_size = osfpos(fp) - mres_seek - 10; |
| | 401 | |
| | 402 | /* go back and fix up the MRES/MREL block header with the block size */ |
| | 403 | osfseek(fp, mres_seek + 4, OSFSK_SET); |
| | 404 | oswp4(buf, mres_size); |
| | 405 | if (osfwb(fp, buf, 4)) |
| | 406 | { |
| | 407 | disp_error(hostifc, "%.*s: error writing header size", |
| | 408 | (int)OSFNMAX, image_fname); |
| | 409 | goto ret_error; |
| | 410 | } |
| | 411 | |
| | 412 | /* seek back to the end of the file */ |
| | 413 | osfseek(fp, 0, OSFSK_END); |
| | 414 | |
| | 415 | /* set up an EOF block */ |
| | 416 | memcpy(buf, "EOF ", 4); |
| | 417 | memset(buf + 4, 0, 4); |
| | 418 | oswp2(buf + 8, VMIMAGE_DBF_MANDATORY); |
| | 419 | |
| | 420 | /* write the EOF block */ |
| | 421 | if (osfwb(fp, buf, 10)) |
| | 422 | { |
| | 423 | disp_error(hostifc, "%.*s: error writing end-of-file block", |
| | 424 | (int)OSFNMAX, image_fname); |
| | 425 | goto ret_error; |
| | 426 | } |
| | 427 | |
| | 428 | /* done with the file */ |
| | 429 | osfcls(fp); |
| | 430 | fp = 0; |
| | 431 | |
| | 432 | /* success */ |
| | 433 | return 0; |
| | 434 | |
| | 435 | ret_error: |
| | 436 | /* close any files we opened */ |
| | 437 | if (fp != 0) |
| | 438 | osfcls(fp); |
| | 439 | if (resfp != 0) |
| | 440 | osfcls(resfp); |
| | 441 | |
| | 442 | /* return an error indication */ |
| | 443 | return 1; |
| | 444 | } |
| | 445 | |
| | 446 | /* |
| | 447 | * Format and display an error message |
| | 448 | */ |
| | 449 | void CResCompMain::disp_error(class CRcHostIfc *hostifc, |
| | 450 | const char *msg, ...) |
| | 451 | { |
| | 452 | char buf[1024]; |
| | 453 | va_list argp; |
| | 454 | |
| | 455 | /* format the message into our buffer */ |
| | 456 | va_start(argp, msg); |
| | 457 | vsprintf(buf, msg, argp); |
| | 458 | va_end(argp); |
| | 459 | |
| | 460 | /* display the formatted message */ |
| | 461 | hostifc->display_error(buf); |
| | 462 | } |
| | 463 | |
| | 464 | /* ------------------------------------------------------------------------ */ |
| | 465 | /* |
| | 466 | * Add a file or directory to a resource list |
| | 467 | */ |
| | 468 | void CRcResList::add_file(const char *fname, const char *alias, |
| | 469 | int recurse) |
| | 470 | { |
| | 471 | char url[OSFNMAX]; |
| | 472 | char search_file[OSFNMAX]; |
| | 473 | int is_dir; |
| | 474 | void *search_ctx; |
| | 475 | |
| | 476 | /* |
| | 477 | * if no alias was specified, convert the filename to a URL and use |
| | 478 | * that as the resource name; otherwise, use the alias without |
| | 479 | * changes |
| | 480 | */ |
| | 481 | if (alias == 0) |
| | 482 | { |
| | 483 | os_cvt_dir_url(url, sizeof(url), fname, FALSE); |
| | 484 | alias = url; |
| | 485 | } |
| | 486 | |
| | 487 | /* |
| | 488 | * if this is a directory, add one entry for each item in the |
| | 489 | * directory |
| | 490 | */ |
| | 491 | search_ctx = os_find_first_file("", fname, |
| | 492 | search_file, sizeof(search_file), |
| | 493 | &is_dir, 0, 0); |
| | 494 | if (search_ctx != 0 && is_dir) |
| | 495 | { |
| | 496 | char fullname[OSFNMAX]; |
| | 497 | |
| | 498 | /* cancel the search - we only needed the one matching file */ |
| | 499 | os_find_close(search_ctx); |
| | 500 | |
| | 501 | /* |
| | 502 | * search through the contents of the directory, and add each |
| | 503 | * entry |
| | 504 | */ |
| | 505 | search_ctx = os_find_first_file(fname, 0, |
| | 506 | search_file, sizeof(search_file), |
| | 507 | &is_dir, fullname, sizeof(fullname)); |
| | 508 | while (search_ctx != 0) |
| | 509 | { |
| | 510 | char full_url[OSFNMAX]; |
| | 511 | size_t len; |
| | 512 | |
| | 513 | /* |
| | 514 | * build the full alias for this file path -- start with the |
| | 515 | * the alias for the directory itself |
| | 516 | */ |
| | 517 | len = strlen(alias); |
| | 518 | memcpy(full_url, alias, len); |
| | 519 | |
| | 520 | /* |
| | 521 | * add a slash to separate the filename from the directory |
| | 522 | * prefix, if the directory path alias doesn't already end |
| | 523 | * in a slash |
| | 524 | */ |
| | 525 | if (len != 0 && full_url[len - 1] != '/') |
| | 526 | full_url[len++] = '/'; |
| | 527 | |
| | 528 | /* add this file name */ |
| | 529 | strcpy(full_url + len, search_file); |
| | 530 | |
| | 531 | /* check whether we found a file or directory */ |
| | 532 | if (is_dir) |
| | 533 | { |
| | 534 | os_specfile_t spec_type; |
| | 535 | |
| | 536 | /* check for a special file */ |
| | 537 | spec_type = os_is_special_file(search_file); |
| | 538 | |
| | 539 | /* |
| | 540 | * It's a directory - if we're allowed to recurse, add |
| | 541 | * all of the directory's contents; otherwise simply |
| | 542 | * ignore it. |
| | 543 | */ |
| | 544 | if (recurse |
| | 545 | && spec_type != OS_SPECFILE_SELF |
| | 546 | && spec_type != OS_SPECFILE_PARENT) |
| | 547 | { |
| | 548 | /* add the subdirectory with a recursive call */ |
| | 549 | add_file(fullname, full_url, TRUE); |
| | 550 | } |
| | 551 | } |
| | 552 | else |
| | 553 | { |
| | 554 | /* add a new entry for this file */ |
| | 555 | add_element(new CRcResEntry(fullname, full_url)); |
| | 556 | } |
| | 557 | |
| | 558 | /* get the next file */ |
| | 559 | search_ctx = os_find_next_file(search_ctx, |
| | 560 | search_file, sizeof(search_file), |
| | 561 | &is_dir, |
| | 562 | fullname, sizeof(fullname)); |
| | 563 | } |
| | 564 | } |
| | 565 | else |
| | 566 | { |
| | 567 | /* if the search succeeded, close it */ |
| | 568 | if (search_ctx != 0) |
| | 569 | os_find_close(search_ctx); |
| | 570 | |
| | 571 | /* it's not a directory - simply add an entry for the file */ |
| | 572 | add_element(new CRcResEntry(fname, alias)); |
| | 573 | } |
| | 574 | } |
| | 575 | |