1 // RESBUILD.C Resource-file building routines
2 // Rex E. Bradford (REX)
4 * $Header: x:/prj/tech/libsrc/res/RCS/resbuild.cpp 1.17 1997/01/16 14:50:50 TOML Exp $
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
12 * Revision 1.15 1996/10/10 16:13:36 TOML
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
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
);
54 #define min(a, b) (((a) < (b)) ? (a) : (b))
56 // -------------------------------------------------------
58 // ResSetComment() sets comment in res header.
60 void ResSetComment(int filenum
, char *comment
)
63 DBG(DSRC_RES_ChkIdRef
,
65 if (resFile
[filenum
].pedit
== NULL
)
67 Warning(("ResSetComment: file %d not open for writing\n", filenum
)); \
72 Spew(DSRC_RES_General
,
73 ("ResSetComment: setting comment for filenum %d to:\n%s\n",
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.
91 extern uchar
*restemp_buffer
;
92 extern int restemp_buffer_size
;
97 static uchar pad
[] = {0, 0, 0, 0, 0, 0, 0, 0};
98 int retval
= 0, old_retval
= 0;
102 ResDirEntry
*pDirEntry
;
105 long size
, sizeTable
;
108 bool buffer_alloc
= FALSE
;
111 DBG(DSRC_RES_ChkIdRef
,
119 prf
= &resFile
[prd
->filenum
];
121 if (prf
->pedit
== NULL
)
123 CriticalMsg("File not opened!");
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",
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
;
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
;
165 if (prd2
->flags
& RDF_COMPOUND
)
167 sizeTable
= REFTABLESIZE(((RefTable
*) p
)->numRefs
);
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
));
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
))
186 pcompbuff
= (void *) Malloc(size
+ EXTRA
);
189 pcompbuff
= (void *) restemp_buffer
;
190 if (pDirEntry
->flags
& RDF_LZW
)
191 compsize
= LzwCompressBuff2Buff(p
, size
, pcompbuff
, size
);
193 compsize
= PkImplodeMemToMem(p
, size
, pcompbuff
, size
);
196 pDirEntry
->flags
&= ~(RDF_LZW
| RDF_PKZIP
);
200 pDirEntry
->csize
= sizeTable
+ compsize
;
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
));
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
;
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
));
227 // Pad to align on data boundary
229 padBytes
= RES_OFFSET_PADBYTES(pDirEntry
->csize
);
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
));
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
);
251 // -------------------------------------------------------------
253 // ResKill() not only deletes a resource from memory, it removes it
254 // from the file too.
259 // Check for valid id
261 DBG(DSRC_RES_ChkIdRef
,
267 Spew(DSRC_RES_Write
, ("ResKill: killing $%x\n", id
));
273 // Make sure file is writeable
277 if (resFile
[prd
->filenum
].pedit
== NULL
)
279 Warning(("ResKill: file %d not open for writing\n", prd
->filenum
)); \
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
)
299 ResDirEntry
*pDirEntry
;
300 long numReclaimed
, sizeReclaimed
;
301 long dataRead
, dataWrite
;
303 ResDirEntry
*peWrite
;
306 prf
= &resFile
[filenum
];
307 if (prf
->pedit
== NULL
)
309 Warning(("ResPack: filenum %d not open for editing\n"));
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)
326 sizeReclaimed
+= pDirEntry
->csize
;
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
);
340 // Now pack directory itself
342 pDirEntry
= (ResDirEntry
*) (prf
->pedit
->pdir
+ 1);
344 for (i
= 0; i
< prf
->pedit
->pdir
->numEntries
; i
++)
348 if (pDirEntry
!= peWrite
)
349 *peWrite
= *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
)
381 bool alloc_buff
= FALSE
;
382 if ((restemp_buffer
== NULL
) || (restemp_buffer_size
< SIZE_RESCOPY
))
384 buff
= (uchar
*) Malloc(SIZE_RESCOPY
);
388 buff
= restemp_buffer
;
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
);
398 writePos
+= sizeCopy
;
405 // --------------------------------------------------------
407 // --------------------------------------------------------
409 // ResEraseIfInFile() erases a resource if it's in a file's directory.
413 // Returns: TRUE if found & erased, FALSE otherwise
415 bool ResEraseIfInFile(Id id
)
419 ResDirEntry
*pDirEntry
;
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
));
431 prf
->pedit
->flags
|= RFF_NEEDSPACK
;
432 if (prf
->pedit
->flags
& RFF_AUTOPACK
)
433 ResPack(prd
->filenum
);