convert line ends
[canaan.git] / prj / tech / libsrc / res / resbuild.cpp
blobaf791d9ce56267a9e08b991e4f527bb64215464a
1 // RESBUILD.C Resource-file building routines
2 // Rex E. Bradford (REX)
3 /*
4 * $Header: x:/prj/tech/libsrc/res/RCS/resbuild.cpp 1.17 1997/01/16 14:50:50 TOML Exp $
5 * $Log: resbuild.cpp $
6 * Revision 1.17 1997/01/16 14:50:50 TOML
7 * stronger error checking
9 * Revision 1.16 1997/01/14 16:37:10 TOML
10 * Initial revision
12 * Revision 1.15 1996/10/10 16:13:36 TOML
13 * msvc port
15 * Revision 1.14 1996/09/14 16:09:57 TOML
16 * Prepared for revision
18 * Revision 1.13 1996/09/14 14:12:45 TOML
19 * Made C++ parser friendly
21 * Revision 1.12 1994/09/22 10:48:52 rex
22 * Modified access to resdesc flags and type, which have moved
24 * Revision 1.11 1994/09/20 17:43:25 xemu
25 * ability to use a pcompbuff
26 * ResKill now works on non/zero file IDs
27 * ResWrite now has a return value
29 * Revision 1.10 1994/06/16 11:06:30 rex
30 * Got rid of RDF_NODROP flag
32 * Revision 1.9 1994/02/17 11:25:32 rex
33 * Moved some stuff out to resmake.c and resfile.c
37 #include <io.h>
38 #include <stdlib.h>
39 #include <string.h>
41 #include <res.h>
42 #include <res_.h>
43 #include <lzw.h>
44 #include <pkzip.h>
45 #include <_res.h>
47 #define CTRL_Z 26 // make sure comment ends with one, so can type a file
49 // Internal prototypes
51 bool ResEraseIfInFile(Id id); // erase item from file
52 void ResCopyBytes(int fd, long writePos, long readPos, long size);
53 #undef min
54 #define min(a, b) (((a) < (b)) ? (a) : (b))
56 // -------------------------------------------------------
58 // ResSetComment() sets comment in res header.
60 void ResSetComment(int filenum, char *comment)
62 ResFileHeader *phead;
63 DBG(DSRC_RES_ChkIdRef,
65 if (resFile[filenum].pedit == NULL)
66 { \
67 Warning(("ResSetComment: file %d not open for writing\n", filenum)); \
68 return;
70 });
72 Spew(DSRC_RES_General,
73 ("ResSetComment: setting comment for filenum %d to:\n%s\n",
74 filenum, comment));
76 phead = &resFile[filenum].pedit->hdr;
77 memset(phead->comment, 0, sizeof(phead->comment));
78 strncpy(phead->comment, comment, sizeof(phead->comment) - 2);
79 phead->comment[strlen(phead->comment)] = CTRL_Z;
81 // -------------------------------------------------------
83 // ResWrite() writes a resource to an open resource file.
84 // This routine assumes that the file position is already set to
85 // the current data position.
86 // Returns the total number of bytes written out, or -1 if there
87 // was a writing error.
89 // id = id to write
91 extern uchar *restemp_buffer;
92 extern int restemp_buffer_size;
94 int ResWrite(Id id)
96 #define EXTRA 250
97 static uchar pad[] = {0, 0, 0, 0, 0, 0, 0, 0};
98 int retval = 0, old_retval = 0;
99 ResDesc *prd;
100 ResDesc2 *prd2;
101 ResFile *prf;
102 ResDirEntry *pDirEntry;
103 uchar *p;
104 void *pcompbuff;
105 long size, sizeTable;
106 long compsize;
107 int padBytes;
108 bool buffer_alloc = FALSE;
109 // Check for errors
111 DBG(DSRC_RES_ChkIdRef,
113 if (!ResCheckId(id))
114 return (-1);
117 prd = RESDESC(id);
118 prd2 = RESDESC2(id);
119 prf = &resFile[prd->filenum];
121 if (prf->pedit == NULL)
123 CriticalMsg("File not opened!");
124 return (-1);
127 // Check if item already in directory, if so erase it
129 ResEraseIfInFile(id);
131 // If directory full, grow it
133 if (prf->pedit->pdir->numEntries == prf->pedit->numAllocDir)
135 Spew(DSRC_RES_Write, ("ResWrite: growing directory of filenum %d\n",
136 prd->filenum));
138 prf->pedit->numAllocDir += DEFAULT_RES_GROWDIRENTRIES;
139 prf->pedit->pdir = (ResDirHeader *) Realloc(prf->pedit->pdir,
140 sizeof(ResDirHeader) + (sizeof(ResDirEntry) * prf->pedit->numAllocDir));
143 // Set resource's file offset
145 prd->offset = RES_OFFSET_REAL2DESC(prf->pedit->currDataOffset);
147 // Fill in directory entry
149 pDirEntry = ((ResDirEntry *) (prf->pedit->pdir + 1)) +
150 prf->pedit->pdir->numEntries;
152 pDirEntry->id = id;
153 pDirEntry->flags = prd2->flags;
154 pDirEntry->type = prd2->type;
155 pDirEntry->size = prd->size;
157 Spew(DSRC_RES_Write, ("ResWrite: writing $%x\n", id));
159 // If compound, write out reftable without compression
161 lseek(prf->fd, prf->pedit->currDataOffset, SEEK_SET);
162 p = (uchar *) prd->ptr;
163 sizeTable = 0;
164 size = prd->size;
165 if (prd2->flags & RDF_COMPOUND)
167 sizeTable = REFTABLESIZE(((RefTable *) p)->numRefs);
168 old_retval = retval;
169 retval += write(prf->fd, p, sizeTable);
170 if (old_retval + sizeTable != retval)
172 Warning(("ResWrite ID %x (RDF_COMPOUND) only wrote %d bytes instead of %d!\n", id, retval - old_retval, sizeTable));
173 return (-1);
175 p += sizeTable;
176 size -= sizeTable;
179 // If compression, try it (may not work out)
181 if (pDirEntry->flags & (RDF_LZW | RDF_PKZIP))
183 if ((restemp_buffer == NULL) || (restemp_buffer_size < size))
185 buffer_alloc = TRUE;
186 pcompbuff = (void *) Malloc(size + EXTRA);
188 else
189 pcompbuff = (void *) restemp_buffer;
190 if (pDirEntry->flags & RDF_LZW)
191 compsize = LzwCompressBuff2Buff(p, size, pcompbuff, size);
192 else
193 compsize = PkImplodeMemToMem(p, size, pcompbuff, size);
194 if (compsize < 0)
196 pDirEntry->flags &= ~(RDF_LZW | RDF_PKZIP);
198 else
200 pDirEntry->csize = sizeTable + compsize;
201 old_retval = retval;
202 retval += write(prf->fd, pcompbuff, compsize);
203 if (old_retval + compsize != retval)
205 Warning(("ResWrite ID %x (RDF_LZW | RDF_PKZIP) only wrote %d bytes instead of %d!\n", id, retval - old_retval, compsize));
206 return (-1);
209 if (buffer_alloc)
210 Free(pcompbuff);
213 // If no compress (or failed to compress well), just write out
215 if (!(pDirEntry->flags & (RDF_LZW | RDF_PKZIP)))
217 pDirEntry->csize = prd->size;
218 old_retval = retval;
219 retval += write(prf->fd, p, size);
220 if (old_retval + size != retval)
222 Warning(("ResWrite ID %x (uncompressed) only wrote %d bytes instead of %d!\n", id, retval - old_retval, size));
223 return (-1);
227 // Pad to align on data boundary
229 padBytes = RES_OFFSET_PADBYTES(pDirEntry->csize);
230 if (padBytes)
232 old_retval = retval;
233 retval += write(prf->fd, pad, padBytes);
234 if (old_retval + padBytes != retval)
236 Warning(("ResWrite ID %x (pad) only wrote %d bytes instead of %d!\n", id, retval - old_retval, padBytes));
237 return (-1);
241 if (tell(prf->fd) & 3)
242 Warning(("ResWrite: misaligned writing!\n"));
244 // Advance dir num entries, current data offset
246 prf->pedit->pdir->numEntries++;
247 prf->pedit->currDataOffset =
248 RES_OFFSET_ALIGN(prf->pedit->currDataOffset + pDirEntry->csize);
249 return (retval);
251 // -------------------------------------------------------------
253 // ResKill() not only deletes a resource from memory, it removes it
254 // from the file too.
256 void ResKill(Id id)
258 ResDesc *prd;
259 // Check for valid id
261 DBG(DSRC_RES_ChkIdRef,
263 if (!ResCheckId(id))
264 return;
267 Spew(DSRC_RES_Write, ("ResKill: killing $%x\n", id));
269 ResDrop(id);
271 prd = RESDESC(id);
273 // Make sure file is writeable
275 DBG(DSRC_RES_Write,
277 if (resFile[prd->filenum].pedit == NULL)
279 Warning(("ResKill: file %d not open for writing\n", prd->filenum)); \
280 return;
284 // If so, erase it
286 ResEraseIfInFile(id);
288 // -------------------------------------------------------------
290 // ResPack() removes holes from a resource file.
292 // filenum = resource filenum (must already be open for create/edit)
294 // Returns: # bytes reclaimed
296 long ResPack(int filenum)
298 ResFile *prf;
299 ResDirEntry *pDirEntry;
300 long numReclaimed, sizeReclaimed;
301 long dataRead, dataWrite;
302 int i;
303 ResDirEntry *peWrite;
304 // Check for errors
306 prf = &resFile[filenum];
307 if (prf->pedit == NULL)
309 Warning(("ResPack: filenum %d not open for editing\n"));
310 return (0);
313 // Set up
315 sizeReclaimed = numReclaimed = 0;
316 dataRead = dataWrite = prf->pedit->pdir->dataOffset;
318 // Scan thru directory, copying over all empty entries
320 pDirEntry = (ResDirEntry *) (prf->pedit->pdir + 1);
321 for (i = 0; i < prf->pedit->pdir->numEntries; i++)
323 if (pDirEntry->id == 0)
325 numReclaimed++;
326 sizeReclaimed += pDirEntry->csize;
328 else
330 if (gResDesc[pDirEntry->id].offset > RES_OFFSET_PENDING)
331 gResDesc[pDirEntry->id].offset = RES_OFFSET_REAL2DESC(dataWrite);
332 if (dataRead != dataWrite)
333 ResCopyBytes(prf->fd, dataWrite, dataRead, pDirEntry->csize);
334 dataWrite = RES_OFFSET_ALIGN(dataWrite + pDirEntry->csize);
336 dataRead = RES_OFFSET_ALIGN(dataRead + pDirEntry->csize);
337 pDirEntry++;
340 // Now pack directory itself
342 pDirEntry = (ResDirEntry *) (prf->pedit->pdir + 1);
343 peWrite = pDirEntry;
344 for (i = 0; i < prf->pedit->pdir->numEntries; i++)
346 if (pDirEntry->id)
348 if (pDirEntry != peWrite)
349 *peWrite = *pDirEntry;
350 peWrite++;
352 pDirEntry++;
354 prf->pedit->pdir->numEntries -= numReclaimed;
356 // Set new current data offset
358 prf->pedit->currDataOffset = dataWrite;
359 lseek(prf->fd, dataWrite, SEEK_SET);
360 prf->pedit->flags &= ~RFF_NEEDSPACK;
362 // Truncate file to just header & data (will be extended later when
363 // write directory on closing)
365 chsize(prf->fd, dataWrite);
367 // Return # bytes reclaimed
369 Spew(DSRC_RES_Write, ("ResPack: reclaimed %d bytes\n", sizeReclaimed));
371 return (sizeReclaimed);
373 #define SIZE_RESCOPY 32768
374 uchar *restemp_buffer = NULL;
375 int restemp_buffer_size = 0;
377 static void ResCopyBytes(int fd, long writePos, long readPos, long size)
379 long sizeCopy;
380 uchar *buff;
381 bool alloc_buff = FALSE;
382 if ((restemp_buffer == NULL) || (restemp_buffer_size < SIZE_RESCOPY))
384 buff = (uchar *) Malloc(SIZE_RESCOPY);
385 alloc_buff = TRUE;
387 else
388 buff = restemp_buffer;
390 while (size > 0)
392 sizeCopy = min(SIZE_RESCOPY, size);
393 lseek(fd, readPos, SEEK_SET);
394 read(fd, buff, sizeCopy);
395 lseek(fd, writePos, SEEK_SET);
396 write(fd, buff, sizeCopy);
397 readPos += sizeCopy;
398 writePos += sizeCopy;
399 size -= sizeCopy;
402 if (alloc_buff)
403 Free(buff);
405 // --------------------------------------------------------
406 // INTERNAL ROUTINES
407 // --------------------------------------------------------
409 // ResEraseIfInFile() erases a resource if it's in a file's directory.
411 // id = id of item
413 // Returns: TRUE if found & erased, FALSE otherwise
415 bool ResEraseIfInFile(Id id)
417 ResDesc *prd;
418 ResFile *prf;
419 ResDirEntry *pDirEntry;
420 int i;
421 prd = RESDESC(id);
422 prf = &resFile[prd->filenum];
423 pDirEntry = (ResDirEntry *) (prf->pedit->pdir + 1);
425 for (i = 0; i < prf->pedit->pdir->numEntries; i++)
427 if (id == pDirEntry->id)
429 Spew(DSRC_RES_Write, ("ResEraseIfInFile: $%x being erased\n", id));
430 pDirEntry->id = 0;
431 prf->pedit->flags |= RFF_NEEDSPACK;
432 if (prf->pedit->flags & RFF_AUTOPACK)
433 ResPack(prd->filenum);
434 return TRUE;
436 pDirEntry++;
439 return FALSE;