root/NEWT0/trunk/src/newt_core/NewtPkg.c

Revision 101, 36.1 kB (checked in by matthiasm, 21 months ago)

Undid previous commit for NewtPkg because it would create another, much worse issue.

  • Property svn:eol-style set to LF
Line 
1/*------------------------------------------------------------------------*/
2/**
3 * @file    NewtPkg.c
4 * @brief   Newton Package Format
5 *
6 * @author  Matthias Melcher
7 * @date    2007-03-25
8 *
9 * Copyright (C) 2007 Matthias Melcher, All rights reserved.
10 */
11
12
13/* header files */
14#include <string.h>
15
16#include "NewtPkg.h"
17#include "NewtErrs.h"
18#include "NewtObj.h"
19#include "NewtEnv.h"
20#include "NewtFns.h"
21#include "NewtVM.h"
22#include "NewtIconv.h"
23
24#include "utils/endian_utils.h"
25
26#include <time.h>
27
28#undef DEBUG_PKG_DIS
29#define DEBUG_PKG
30#ifdef DEBUG_PKG
31#   include "NewtPrint.h"
32#endif
33
34
35/* macros */
36
37/// Test first 8 bytes for any of the package signatures
38#define PkgIsPackage(data) ((strncmp(data, "package0", 8)==0) || (strncmp(data, "package1", 8)==0))
39
40
41/* types */
42
43/// info refs are a shorthand version of pointers into a userdata area of the package
44typedef struct {
45    uint16_t    offset;         ///< offset from the flexible size header block
46    uint16_t    size;           ///< size of data block
47} pkg_info_ref_t;
48
49/// package header structure
50typedef struct {
51    uint8_t     sig[8];         ///< 8 byte signature
52    uint32_t    type;           ///< 4 bytes describing package class
53    uint32_t    flags;          ///< describing how to store and load the package
54    uint32_t    version;        ///< user definable; higher numbers are newer version
55    pkg_info_ref_t copyright;   ///< utf16 copyright message
56    pkg_info_ref_t name;        ///< utf16 package name
57    uint32_t    size;           ///< entire size of package
58    uint32_t    date;           ///< creation date
59    uint32_t    reserverd2;
60    uint32_t    reserverd3;
61    uint32_t    directorySize;  ///< size in bytes of the following block
62    uint32_t    numParts;       ///< number of parts in this package
63} pkg_header_t;
64
65//// package part descriptor
66typedef struct {
67    uint32_t    offset;         ///< offset to beginning of part section
68    uint32_t    size;           ///< size of part in bytes
69    uint32_t    size2;          ///< as above (maybe once intended to hold the compressed size)
70    uint32_t    type;           ///< type of part
71    uint32_t    reserverd1;
72    uint32_t    flags;          ///< some more information about this part
73    pkg_info_ref_t info;        ///< activation data; type depends on part type
74    uint32_t    reserverd2;
75} pkg_part_t;
76
77/// structure to manage the package reading process
78typedef struct {
79    uint8_t     pkg_version;    ///< rw corrsponds to the last charof the signature
80    uint8_t *   data;           ///< rw package data
81    uint32_t    size;           ///< rw size of package
82    uint32_t    data_size;      ///< w  size of data block
83    pkg_header_t *header;       ///< r  pointer to the package header
84    uint32_t    header_size;    ///< w  size of header structure w/o var data
85    uint8_t *   var_data;       ///< r  extra data for header
86    uint32_t    var_data_size;  ///< w  size of variable length data block
87    uint32_t    directory_size; ///< w  size of all headers plus var data
88    uint32_t    num_parts;      ///< r  number of parts in package
89    pkg_part_t* part_headers;   ///< r  pointer to first part header - can be used as an array
90    pkg_part_t* part_header;    ///< r  header of current part
91    uint8_t *   part;           ///< r  start of current part data
92    uint32_t    part_offset;    ///< r  offset of current part to the beginning of data
93    uint32_t    part_header_offset; ///< r  offset of current part header
94    newtRefVar  instances;      ///< rw array holding the previously generated instance of any ref per part
95    newtRefVar  precedents;     ///< w  array referencing the instances array
96    newtErr     lastErr;        ///< r  a way to return error from deep below
97#ifdef HAVE_LIBICONV
98    iconv_t     from_utf16;     ///< r  strings in compatible packages are UTF16
99    iconv_t     to_utf16;       ///< w  strings in compatible packages are UTF16
100#endif /* HAVE_LIBICONV */
101} pkg_stream_t;
102
103
104/* functions */
105
106static int32_t  PkgArraySearch(newtRefArg array, newtRefArg r);
107static uint32_t PkgAlign(pkg_stream_t *pkg, uint32_t offset);
108static uint32_t PkgGetSlotInt(newtRefArg frame, newtRefArg name, uint32_t def);
109
110static newtRef  PkgPartGetPrecedent(pkg_stream_t *pkg, newtRefArg ref);
111static void     PkgPartSetPrecedent(pkg_stream_t *pkg, newtRefArg ref, newtRefArg val);
112
113static void     PkgMakeRoom(pkg_stream_t *pkg, uint32_t offset, uint32_t size);
114static void     PkgWriteData(pkg_stream_t *pkg, uint32_t offset, void *data, uint32_t size);
115static void     PkgWriteU32(pkg_stream_t *pkg, uint32_t offset, uint32_t v);
116static void     PgkWriteVarData(pkg_stream_t *pkg, uint32_t offset, newtRefVar frame, newtRefVar sym);
117static newtRef  PkgWriteFrame(pkg_stream_t *pkg, newtRefArg frame);
118static newtRef  PkgWriteArray(pkg_stream_t *pkg, newtRefArg array);
119static newtRef  PkgWriteBinary(pkg_stream_t *pkg, newtRefArg obj);
120static newtRef  PkgWriteObject(pkg_stream_t *pkg, newtRefArg obj);
121static void     PkgWritePart(pkg_stream_t *pkg, newtRefArg part);
122
123static uint32_t PkgReadU32(uint8_t *d) ;
124static newtRef  PkgPartGetInstance(pkg_stream_t *pkg, uint32_t p_obj);
125static void     PkgPartSetInstance(pkg_stream_t *pkg, uint32_t p_obj, newtRefArg r);
126static newtRef  PkgReadRef(pkg_stream_t *pkg, uint32_t p_obj);
127static newtRef  PkgReadBinaryObject(pkg_stream_t *pkg, uint32_t p_obj);
128static newtRef  PkgReadArrayObject(pkg_stream_t *pkg, uint32_t p_obj);
129static newtRef  PkgReadFrameObject(pkg_stream_t *pkg, uint32_t p_obj);
130static newtRef  PkgReadObject(pkg_stream_t *pkg, uint32_t p_obj);
131static newtRef  PkgReadNOSPart(pkg_stream_t *pkg);
132static newtRef  PkgReadPart(pkg_stream_t *pkg, int32_t index);
133static newtRef  PkgReadVardataBinary(pkg_stream_t *pkg, pkg_info_ref_t *info_ref);
134static newtRef  PkgReadVardataString(pkg_stream_t *pkg, pkg_info_ref_t *info_ref);
135static newtRef  PkgReadHeader(pkg_stream_t *pkg);
136
137
138/*------------------------------------------------------------------------*/
139/** Duplicate of same function in NSOF.
140 * Maybe we should make the original file publicly accessible
141 *
142 * @see NewtSearchArray(newtRefArg array, newtRefArg r)
143 */
144int32_t PkgArraySearch(newtRefArg array, newtRefArg r)
145{
146    newtRef *   slots;
147    uint32_t    len;
148    uint32_t    i;
149
150    len = NewtArrayLength(array);
151    slots = NewtRefToSlots(array);
152
153    for (i = 0; i < len; i++)
154    {
155        if (slots[i] == r)
156            return i;
157    }
158
159    return -1;
160}
161
162/*------------------------------------------------------------------------*/
163/** Verify if the give reference was already written to the package.
164 *
165 * @param pkg       [inout] the package
166 * @param ref       [in] the reference that we try to find
167 *
168 * @retval  ref to the previously written object
169 * @retval  or kNewtRefUnbind if the object still needs to be written
170 */
171newtRef PkgPartGetPrecedent(pkg_stream_t *pkg, newtRefArg ref)
172{
173    int32_t ix = PkgArraySearch(pkg->precedents, ref);
174    if (ix>=0) {
175        return NewtGetArraySlot(pkg->instances, ix);
176    } else {
177        return kNewtRefUnbind;
178    }
179}
180
181/*------------------------------------------------------------------------*/
182/** Remember that a specific reference was already written.
183 *
184 * @param pkg       [inout] the package
185 * @param ref       [in] the original reference in memory
186 * @param val       [in] the reference needed to find this object in the package
187 */
188void PkgPartSetPrecedent(pkg_stream_t *pkg, newtRefArg ref, newtRefArg val)
189{
190    uint32_t n = NewtArrayLength(pkg->instances);
191    // the code below gets horribly slow and fragments memory
192    // we should consider implementing a binary search tree at some point
193    NewtInsertArraySlot(pkg->instances, n, val);
194    NewtInsertArraySlot(pkg->precedents, n, ref);
195}
196
197/*------------------------------------------------------------------------*/
198/** Align the given value to 8 or 4 bytes.
199 *
200 * @param pkg       [inout] the package
201 * @param offset    [in] offset to be aligned
202 *
203 * @retval  new offset aligned to 8 bytes, or 4 bytes for the advance format
204 *
205 * @todo We shoudld add 4-byte alignment support at some point, so the
206 *       generated package is a bit smaller
207 */
208uint32_t PkgAlign(pkg_stream_t *pkg, uint32_t offset)
209{
210    return (offset+7)&(~7);
211}
212
213/*------------------------------------------------------------------------*/
214/** Convenience function to get the integer value of a slot.
215 *
216 * @param frame     [in] find the slot in this frame
217 * @param name      [in] name of slot
218 * @param def       [in] default value if slot was not found
219 *
220 * @retval  value read if found, or default value if not found
221 */
222uint32_t PkgGetSlotInt(newtRefArg frame, newtRefArg name, uint32_t def)
223{
224    newtRef slot;
225    int32_t ix;
226   
227    ix = NewtFindSlotIndex(frame, name);
228    if (ix<0)
229        return def;
230    slot = NewtGetArraySlot(frame, ix);
231    if (NewtRefIsInteger(slot)) {
232        return NewtRefToInteger(slot);
233    } else {
234        return def;
235    }
236}
237
238/*------------------------------------------------------------------------*/
239/** Make some room for more data in the package
240 *
241 * @param pkg       [inout] the package
242 * @param offset    [in] offset where we will need room
243 * @param size  [in] number of bytes that we will need
244 */
245void PkgMakeRoom(pkg_stream_t *pkg, uint32_t offset, uint32_t size)
246{
247    uint32_t new_size = offset+size;
248
249    if (pkg->data_size<new_size) {
250        uint32_t os = pkg->data_size;
251        uint32_t ns = (new_size+16383) & (~16383); // alocate in 16k blocks
252        pkg->data = realloc(pkg->data, ns);
253        memset(pkg->data+os, 0xbf, ns-os); // filler byte
254        pkg->data_size = ns;
255    }
256
257    if (pkg->size<new_size)
258        pkg->size = new_size;
259}
260
261/*------------------------------------------------------------------------*/
262/** Write arbitrary data into the package at any position
263 *
264 * @param pkg       [inout] the package
265 * @param offset    [in] offset to the beginning of data
266 * @param data      [in] data to be copied to package
267 * @param size      [in] size of data block
268 */
269void PkgWriteData(pkg_stream_t *pkg, uint32_t offset, void *data, uint32_t size)
270{
271    if (pkg->data_size<offset+size)
272        PkgMakeRoom(pkg, offset, size);
273    if (pkg->size<offset+size)
274        pkg->size = offset+size;
275    memcpy(pkg->data+offset, data, size);
276}
277
278/*------------------------------------------------------------------------*/
279/** Write a 32 bit integer into the package at any position
280 *
281 * @param pkg       [inout] the package
282 * @param offset    [in] offset to the beginning of data
283 * @param v         [in] value to be written
284 */
285void PkgWriteU32(pkg_stream_t *pkg, uint32_t offset, uint32_t v)
286{
287    v = htonl(v);
288    PkgWriteData(pkg, offset, &v, 4);
289}
290
291/*------------------------------------------------------------------------*/
292/** Write the data in the object into the vardata section and generate the info ref
293 *
294 * @param pkg       [inout] the package
295 * @param offset    [in] where to write the info ref
296 * @param frame     [in] frame containing the data
297 * @param sym       [in] symbol of slot containing the data
298 */
299void PgkWriteVarData(pkg_stream_t *pkg, uint32_t offset, newtRefVar frame, newtRefVar sym)
300{
301    newtRef info;
302    uint32_t ix;
303   
304    PkgWriteU32(pkg, offset, 0);
305
306    ix = NewtFindSlotIndex(frame, sym);
307    if (ix<0)
308        return;
309    info = NewtGetFrameSlot(frame, ix);
310    if (NewtRefIsBinary(info)) {
311        uint32_t size = NewtBinaryLength(info);
312        uint8_t *data = NewtRefToBinary(info);
313        pkg_info_ref_t info_ref;
314
315#       ifdef HAVE_LIBICONV
316            if (NewtRefIsString(info)) {
317                size_t buflen;
318                char *buf = NewtIconv(pkg->to_utf16, data, size, &buflen);
319                if (buf) {
320                    size = buflen;
321                    data = buf;
322                }
323            }
324#       endif /* HAVE_LIBICONV */
325
326        info_ref.offset = htons(pkg->var_data_size);
327        info_ref.size = htons(size);
328
329        PkgWriteData(pkg, pkg->header_size + pkg->var_data_size, data, size);
330        PkgWriteData(pkg, offset, &info_ref, 4);
331
332        pkg->var_data_size += size;     
333    }
334}
335
336/*------------------------------------------------------------------------*/
337/** Append a frame object to the end of the Package
338 *
339 * @param pkg       [inout] the package
340 * @param frame     [in] the frame that we will write
341 *
342 * @retval  offset to the beginning of the object in the package file
343 */
344newtRef PkgWriteFrame(pkg_stream_t *pkg, newtRefArg frame)
345{
346    uint32_t dst, size, i, n;
347    newtRef map, map_pos;
348    newtRef slot, slot_pos;
349
350    // calculate the size of this chunk
351    dst = PkgAlign(pkg, pkg->size);
352    n = NewtFrameLength(frame);
353    size = (n+3)*4;
354
355    // make room for the entire chunk and write the header
356    PkgMakeRoom(pkg, dst, size);
357    PkgWriteU32(pkg, dst, (size<<8) | 0x40 | kObjSlotted | kObjFrame);
358    PkgWriteU32(pkg, dst+4, 0);
359
360    // recursively add all arrays making up the map
361    map = NewtFrameMap(frame);
362    map_pos = PkgWriteObject(pkg, map);
363    PkgWriteU32(pkg, dst+8, map_pos);
364
365    // now add all slots and fill in the rest of our chunk
366    for (i=0; i<n; i++) {
367        slot = NewtGetFrameSlot(frame, i);
368        slot_pos = PkgWriteObject(pkg, slot);
369        PkgWriteU32(pkg, dst+12+4*i, slot_pos);
370    }
371
372    return NewtMakePointer(dst);
373}
374
375/*------------------------------------------------------------------------*/
376/** Append an array object to the end of the Package
377 *
378 * @param pkg       [inout] the package
379 * @param array     [in] the array that we will write
380 *
381 * @retval  offset to the beginning of the object in the package file
382 */
383newtRef PkgWriteArray(pkg_stream_t *pkg, newtRefArg array)
384{
385    uint32_t dst, size, i, n;
386    newtRef klass, klass_pos;
387    newtRef slot, slot_pos;
388
389    // calculate the size of this chunk
390    dst = PkgAlign(pkg, pkg->size);
391    n = NewtArrayLength(array);
392    size = (n+3)*4;
393
394    // make room for the entire chunk and write the header
395    PkgMakeRoom(pkg, dst, size);
396    PkgWriteU32(pkg, dst, (size<<8) | 0x40 | kObjSlotted);
397    PkgWriteU32(pkg, dst+4, 0);
398
399    // add the class information
400    klass = NcClassOf(array);
401    klass_pos = PkgWriteObject(pkg, klass);
402    PkgWriteU32(pkg, dst+8, klass_pos);
403
404    // now add all slots and fill in the rest of our chunk
405    for (i=0; i<n; i++) {
406        slot = NewtGetArraySlot(array, i);
407        slot_pos = PkgWriteObject(pkg, slot);
408        PkgWriteU32(pkg, dst+12+4*i, slot_pos);
409    }
410
411    return NewtMakePointer(dst);
412}
413
414/*------------------------------------------------------------------------*/
415/** Append a binary object to the end of the Package
416 *
417 * @param pkg       [inout] the package
418 * @param obj       [in] the binary object that we will write
419 *
420 * @retval  offset to the beginning of the object in the package file
421 */
422newtRef PkgWriteBinary(pkg_stream_t *pkg, newtRefArg obj)
423{
424    uint32_t dst, size;
425    uint8_t *data;
426    newtRef klass, klass_ref = kNewtRefUnbind;
427
428    // calculate the size of this chunk
429    dst = PkgAlign(pkg, pkg->size);
430    size = NewtBinaryLength(obj);
431    data = NewtRefToBinary(obj);
432
433    // make room for the binary chunk and write the header
434    if (NewtRefIsSymbol(obj)) {
435        size = NewtSymbolLength(obj)+5; // remember the trailing zero!
436    } else if (NewtRefIsString(obj)) {
437#       ifdef HAVE_LIBICONV
438            size_t buflen;
439            char *buf = NewtIconv(pkg->to_utf16, data, size, &buflen);
440            if (buf) {
441                size = buflen;
442                data = buf;
443            }
444#       endif /* HAVE_LIBICONV */
445    }
446    size += 12;
447
448    // make room for the binary chunk and write the header
449    PkgMakeRoom(pkg, dst, size);
450    PkgWriteU32(pkg, dst, (size<<8) | 0x40);
451    PkgWriteU32(pkg, dst+4, 0);
452
453    // symbols have special handling to avoid recursion
454    if (NewtRefIsSymbol(obj)) {
455        PkgWriteU32(pkg, dst+8, kNewtSymbolClass);
456        PkgWriteU32(pkg, dst+12, NewtRefToHash(obj)); // make sure the hash has the right endianness
457        PkgWriteData(pkg, dst+16, (uint8_t*)NewtSymbolGetName(obj), size-16);
458        return NewtMakePointer(dst);
459    }
460
461    // add the class information
462    klass = NcClassOf(obj);
463    klass_ref = PkgWriteObject(pkg, klass);
464    PkgWriteU32(pkg, dst+8, klass_ref);
465
466    // copy the binary data over
467    if (klass==NSSYM0(int32)) {
468        uint32_t *s = (uint32_t*)data;
469        uint32_t  v = htonl(*s);
470        PkgWriteData(pkg, dst+12, &v, sizeof(v));
471    } else if (klass==NSSYM0(real)) {
472        // this code fails miserably if 'double' is not an 8-byte IEEE value!
473        double *s = (double*)data;
474        double  v = htond(*s);
475        PkgWriteData(pkg, dst+12, &v, sizeof(v));
476    } else {
477        PkgWriteData(pkg, dst+12, data, size-12);
478    }
479
480    return NewtMakePointer(dst);
481}
482
483/*------------------------------------------------------------------------*/
484/** Append the object to the end of the Package
485 *
486 * @param pkg       [inout] the package
487 * @param obj       [in] the object that we will write
488 *
489 * @retval  offset to the beginning of the object in the package file
490 */
491newtRef PkgWriteObject(pkg_stream_t *pkg, newtRefArg obj)
492{
493    uint32_t dst = pkg->size;
494    newtRef prec;
495
496    // FIXME add handling named magic pointers here
497    if (NewtRefIsImmediate(obj)) {
498        // immediates have the same form in memory as in packages
499        // immediates include magic pointers
500        return obj;
501    }
502   
503    prec = PkgPartGetPrecedent(pkg, obj);
504    if (prec!=kNewtRefUnbind) {
505        return prec;
506    }
507
508    if (NewtRefIsFrame(obj)) {
509        dst = PkgWriteFrame(pkg, obj);
510    } else if (NewtRefIsArray(obj)) {
511        dst = PkgWriteArray(pkg, obj);
512    } else if (NewtRefIsBinary(obj)) {
513        dst = PkgWriteBinary(pkg, obj);
514    } else {
515#       ifdef DEBUG_PKG
516            // we do not know how to write this object
517            printf("*** unsupported write: ");
518            NewtPrintObject(stdout, obj);
519#       endif
520        return kNewtRefNIL; // do not create a precedent
521    }
522
523    // make this ref available for later incarnations of the same object
524    PkgPartSetPrecedent(pkg, obj, dst);
525
526    return dst;
527}
528
529/*------------------------------------------------------------------------*/
530/** Create a part in package format based on this object.
531 *
532 * This function makes heavy use of the "pkg" structure, updating all
533 * members to allow writing multiple consecutive parts by repeatedly
534 * caling this function. Multiple part packages are untested.
535 *
536 * @param pkg       [inout] the package
537 * @param part      [in] object containing part data
538 *
539 * @retval  ref to binary part data
540 */
541void PkgWritePart(pkg_stream_t *pkg, newtRefArg part)
542{
543    uint32_t    dst = pkg->part_offset;
544    uint32_t    hdr = pkg->part_header_offset;
545    int32_t     ix;
546    newtRef     data;
547    uint32_t    part_size;
548
549    ix = NewtFindSlotIndex(part, NSSYM(data));
550    if (ix<0)
551        return;
552    data = NewtGetFrameSlot(part, ix);
553
554    pkg->instances = NewtMakeArray(kNewtRefUnbind, 0);
555    pkg->precedents = NewtMakeArray(kNewtRefUnbind, 0);
556
557    PkgMakeRoom(pkg, dst, 16);
558    PkgWriteU32(pkg, dst,    0x00001041);
559    PkgWriteU32(pkg, dst+4,  0x00000000);
560    PkgWriteU32(pkg, dst+8,  0x00000002);
561    PkgWriteU32(pkg, dst+12, PkgWriteObject(pkg, data));
562
563    NewtSetLength(pkg->precedents, 0);
564    NewtSetLength(pkg->instances, 0);
565
566    PkgMakeRoom(pkg, PkgAlign(pkg, pkg->size), 0);
567    part_size = pkg->size - pkg->part_offset;
568        // offset
569    PkgWriteU32(pkg, hdr, pkg->part_offset - pkg->directory_size);
570        // size
571    PkgWriteU32(pkg, hdr+4, part_size);
572        // size2
573    PkgWriteU32(pkg, hdr+8, part_size);
574        // type
575    PkgWriteU32(pkg, hdr+12, PkgGetSlotInt(part, NSSYM(type), 0x666f726d)); // "form"
576        // reserved1
577    PkgWriteU32(pkg, hdr+16, 0);
578        // flags
579    PkgWriteU32(pkg, hdr+20, PkgGetSlotInt(part, NSSYM(flags), 0));
580        // reserved2
581    PkgWriteU32(pkg, hdr+28, 0);
582
583    pkg->part_offset += part_size;
584}
585
586/*------------------------------------------------------------------------*/
587/** Create a new binary object that contains the object tree in package format.
588 *
589 * This function creates a binary object, containing the representaion of
590 * a whole hierarchy of objects in the Newton package format. The binary
591 * data can be written directly to disk to create a Newton readable .pkg file.
592 *
593 * NewtWritePkg was tested on hierarchies created by NewtReadPackage, reading
594 * a random bunch of .pkg files containing Newton Script applications. The
595 * packages created were identiacla to the original packages.
596 *
597 * @todo    NewtWritePkg does not support a relocation table yet which may
598 *          be needed to save native function of a higher complexity.
599 * @todo    Error handling is not yet implemented.
600 * @todo    Named magic poiners are not supported yet.
601 * @todo    Only NOS parts are currently supported. We still must implement
602 *          Protocol parts and Raw parts.
603 *
604 * @param rpkg  [in] object hierarchy describing the package
605 *
606 * @retval  binary object with package
607 */
608newtRef NewtWritePkg(newtRefArg package)
609{
610    pkg_stream_t    pkg;
611    int32_t         num_parts, i, ix;
612    newtRef         parts, result;
613
614    // setup pkg_stream_t
615    memset(&pkg, 0, sizeof(pkg));
616
617#   ifdef HAVE_LIBICONV
618    {   char *encoding = NewtDefaultEncoding();
619        pkg.to_utf16 = iconv_open("UTF-16BE", encoding);
620    }
621#   endif /* HAVE_LIBICONV */
622
623    // find the array of parts that we will write
624    ix = NewtFindSlotIndex(package, NSSYM(parts));
625    if (ix>=0) {
626        parts = NewtGetFrameSlot(package, ix);
627        num_parts = NewtFrameLength(parts);
628        pkg.header_size = sizeof(pkg_header_t) + num_parts * sizeof(pkg_part_t);
629
630        // start setting up the header with whatever we know
631            // sig
632        PkgWriteData(&pkg, 0, "package0", 8);
633        pkg.data[7] = (uint8_t)('0' + PkgGetSlotInt(package, NSSYM(pkg_version), 0));
634            // type
635        PkgWriteU32(&pkg, 8, PkgGetSlotInt(package, NSSYM(type), 0x78787878)); // "xxxx"
636            // flags
637        PkgWriteU32(&pkg, 12, PkgGetSlotInt(package, NSSYM(flags), 0));
638            // version
639        PkgWriteU32(&pkg, 16, PkgGetSlotInt(package, NSSYM(version), 0));
640            // copyright
641        PgkWriteVarData(&pkg, 20, package, NSSYM(copyright));
642            // name
643        PgkWriteVarData(&pkg, 24, package, NSSYM(name));
644            // date
645        PkgWriteU32(&pkg, 32, time(0L)+2082844800);
646            // reserved2
647        PkgWriteU32(&pkg, 36, 0);
648            // reserved3
649        PkgWriteU32(&pkg, 40, 0);
650            // numParts
651        PkgWriteU32(&pkg, 48, num_parts);
652
653        // calculate the size of the header so we can correctly set our refs in the parts
654        for (i=0; i<num_parts; i++) {
655            newtRef part = NewtGetArraySlot(parts, i);
656            PgkWriteVarData(&pkg, sizeof(pkg_header_t) + i*sizeof(pkg_part_t) + 24, part, NSSYM(info));
657        }
658
659        // the original file has this (c) message embedded
660        {   
661#ifdef _MSC_VER
662            char msg[] = "Newton� ToolKit Package � 1992-1997, Apple Computer, Inc.";
663#else
664            char msg[] = "Newtonェ ToolKit Package ゥ 1992-1997, Apple Computer, Inc.";
665#endif
666            PkgWriteData(&pkg, pkg.header_size + pkg.var_data_size, msg, sizeof(msg));
667            pkg.var_data_size += sizeof(msg);
668        }
669
670        pkg.part_offset = pkg.directory_size = PkgAlign(&pkg, pkg.header_size + pkg.var_data_size);
671            // directorySize
672        PkgWriteU32(&pkg, 44, pkg.directory_size);
673
674        // create all parts
675        for (i=0; i<num_parts; i++) {
676            newtRef part = NewtGetArraySlot(parts, i);
677            pkg.part_header_offset = sizeof(pkg_header_t) + i*sizeof(pkg_part_t);
678            PkgWritePart(&pkg, part);
679        }
680    }
681
682    // finish filling in the header
683        // size
684    PkgWriteU32(&pkg, 28, pkg.size);
685
686    result = NewtMakeBinary(NSSYM(package), pkg.data, pkg.size, false);
687
688    // clean up our allocations
689    if (pkg.data)
690        free(pkg.data);
691
692#   ifdef HAVE_LIBICONV
693        iconv_close(pkg.to_utf16);
694#   endif /* HAVE_LIBICONV */
695
696    return result;
697}
698
699/*------------------------------------------------------------------------*/
700/** Endian-neutral conversion of four bytes into one uint32
701 *
702 * @param d     [in] pointer to byte array
703 *
704 * @retval  uint32 assembled from four bytes
705 */
706uint32_t PkgReadU32(uint8_t *d)
707{
708    return ((d[0]<<24)|(d[1]<<16)|(d[2]<<8)|(d[3]));
709}
710
711/*------------------------------------------------------------------------*/
712/** Check if there was already an object created for the given offset.
713 *
714 * @param pkg       [inout] the package
715 * @param p_obj     [in] offset to object
716 *
717 * @retval  ref to previously create object or kNewtRefUnbind
718 */
719newtRef PkgPartGetInstance(pkg_stream_t *pkg, uint32_t p_obj)
720{
721    uint32_t ix = (p_obj - pkg->part_offset) / 4;
722    return NewtGetArraySlot(pkg->instances, ix);
723}
724
725/*------------------------------------------------------------------------*/
726/** Set the object ref for the given offset in the file
727 *
728 * @param pkg       [inout] the package
729 * @param r         [in] ref to new object
730 * @param p_obj     [in] offset into data block
731 */
732void PkgPartSetInstance(pkg_stream_t *pkg, uint32_t p_obj, newtRefArg r)
733{
734    uint32_t ix = (p_obj - pkg->part_offset) / 4;
735    NewtSetArraySlot(pkg->instances, ix, r);
736}
737
738/*------------------------------------------------------------------------*/
739/** Interprete a ref in the Package data block.
740 *
741 * @param pkg       [inout] the package
742 * @param p_obj     [in] offset to Ref data relative to package start
743 *
744 * @retval  Newt version of Package Ref
745 */
746newtRef PkgReadRef(pkg_stream_t *pkg, uint32_t p_obj)
747{
748    uint32_t ref = PkgReadU32(pkg->data + p_obj);
749    newtRef result = kNewtRefNIL;
750
751    switch (ref&3) {
752    case 0: // integer
753        result = NSINT(ref>>2);
754        break;
755    case 1: // pointer
756        result = PkgReadObject(pkg, ref&~3);
757        break;
758    case 2: // special
759        // special refs are immedites, encoded in the same format in
760        // memory as in package files (at least for all instances I could find)
761        result = ref;
762        break;
763    case 3: // magic pointer
764        // FIXME we must implement special code for name magic pointers here!
765        result = ref; // already a correct magic pointer
766        break;
767    }
768
769    return result;
770}
771
772/*------------------------------------------------------------------------*/
773/** Generate a binary object from Package data
774 *
775 * @param pkg       [inout] the package
776 * @param p_obj     [in] offset to binary data object relative to package start
777 *
778 * @retval  Newt object version of binary object
779 */
780newtRef PkgReadBinaryObject(pkg_stream_t *pkg, uint32_t p_obj)
781{
782    uint32_t size = PkgReadU32(pkg->data + p_obj) >> 8;
783    newtRef klass, result = kNewtRefNIL;
784    newtRef ins = NSSYM0(instructions);
785
786    klass = PkgReadRef(pkg, p_obj+8);
787
788    if (klass==kNewtSymbolClass) {
789        result = NewtMakeSymbol(pkg->data + p_obj + 16);
790    } else if (klass==NSSYM0(string)) {
791        char *src = pkg->data + p_obj + 12;
792        int sze = size-12;
793#       ifdef HAVE_LIBICONV
794            size_t buflen;
795            char *buf = NewtIconv(pkg->from_utf16, src, sze, &buflen);
796            if (buf) {
797                result = NewtMakeString2(buf, buflen-1, true); // NewtMakeString2 appends another null
798            }
799#       endif /* HAVE_LIBICONV */
800        if (result==kNewtRefNIL)
801            result = NewtMakeString2(src, sze, true);
802    } else if (klass==NSSYM0(int32)) {
803        uint32_t v = PkgReadU32(pkg->data + p_obj + 12);
804        result = NewtMakeInt32(v);
805    } else if (klass==NSSYM0(real)) {
806        double *v = (double*)(pkg->data + p_obj + 12);
807        result = NewtMakeReal(ntohd(*v));
808    } else if (klass==NSSYM0(instructions)) {
809        result = NewtMakeBinary(klass, pkg->data + p_obj + 12, size-12, true);
810#       ifdef DEBUG_PKG_DIS
811            printf("*** PkgReader: PkgReadBinaryObject - dumping byte code\n");
812            NVMDumpBC(stdout, result);
813#       endif
814    } else {
815#       ifdef DEBUG_PKG
816            // This output is helpful to find more binary classes that may need
817            // endianness fixes like the floating point class
818            if (klass) {
819                printf("*** PkgReader: PkgReadBinaryObject - unknown class ");
820                NewtPrintObject(stdout, klass);
821            }
822#       endif
823        result = NewtMakeBinary(klass, pkg->data + p_obj + 12, size-12, true);
824    }
825
826    return result;
827}
828
829/*------------------------------------------------------------------------*/
830/** Generate an Array Object from Package data
831 *
832 * @param pkg       [inout] the package
833 * @param p_obj     [in] offset to array data relative to package start
834 *
835 * @retval  Newt object version of package Array
836 */
837newtRef PkgReadArrayObject(pkg_stream_t *pkg, uint32_t p_obj)
838{
839    uint32_t size = PkgReadU32(pkg->data + p_obj) >> 8;
840    uint32_t num_slots = size/4 - 3;
841    uint32_t i;
842    newtRef array, klass;
843
844    klass = PkgReadRef(pkg, p_obj+8);
845    array = NewtMakeArray(klass, num_slots);
846
847    if (NewtRefIsNotNIL(array)) {
848        for (i=0; i<num_slots; i++) {
849            NewtSetArraySlot(array, i, PkgReadRef(pkg, p_obj+12 + 4*i));
850        }
851    }
852
853    return array;
854}
855
856
857/*------------------------------------------------------------------------*/
858/** Generate a Frame Object from Package data
859 *
860 * @param pkg       [inout] the package
861 * @param p_obj     [in] offset to frame data relative to package start
862 *
863 * @retval  Newt object version of package Frame
864 */
865newtRef PkgReadFrameObject(pkg_stream_t *pkg, uint32_t p_obj)
866{
867    uint32_t size = PkgReadU32(pkg->data + p_obj) >> 8;
868    uint32_t i, num_slots = size/4 - 3;
869    newtRef frame = kNewtRefNIL, map;
870
871    map = PkgReadRef(pkg, p_obj+8);
872
873    frame = NewtMakeFrame(map, num_slots);
874
875    if (NewtRefIsNotNIL(frame)) {
876
877        newtRef *slot = NewtRefToSlots(frame);
878        for (i=0; i<num_slots; i++) {
879            slot[i] = PkgReadRef(pkg, p_obj+12 + 4*i);
880        }
881    }
882
883    return frame;
884}
885
886/*------------------------------------------------------------------------*/
887/** Recursively read the object at the given offset
888 *
889 * @param pkg       [inout] the package
890 * @param p_obj     [in] offset to object relative to package start
891 *
892 * @retval  Newt object version of package object
893 */
894newtRef PkgReadObject(pkg_stream_t *pkg, uint32_t p_obj)
895{
896    uint32_t obj = PkgReadU32(pkg->data + p_obj);
897    newtRef ret = PkgPartGetInstance(pkg, p_obj);
898
899    // avoid generating objects twice
900    if (ret!=kNewtRefUnbind)
901        return ret;
902
903    // create an object based on its low 8 bit type
904    switch (obj & 0xff) {
905    case 0x40: // binary object
906        ret = PkgReadBinaryObject(pkg, p_obj);
907        break;
908    case 0x40 | kObjSlotted: // array
909        ret = PkgReadArrayObject(pkg, p_obj);
910        break;
911    case 0x40 | kObjSlotted | kObjFrame: // frame
912        ret = PkgReadFrameObject(pkg, p_obj);
913        break;
914    case 0x40 | kObjFrame: // not defined
915    default:
916#       ifdef DEBUG_PKG
917            printf("*** PkgReader: PkgReadObject - unsupported object 0x%08x\n", obj);
918#       endif
919        break;
920    }
921
922    // remember that we created this object
923    PkgPartSetInstance(pkg, p_obj, ret);
924
925    return ret;
926}
927
928/*------------------------------------------------------------------------*/
929/** Read a NOS part from the package file.
930 *
931 * @param pkg       [inout] the package
932 *
933 * @retval  Newt object with contents of NOS part
934 */
935newtRef PkgReadNOSPart(pkg_stream_t *pkg)
936{
937    uint32_t    p_obj;
938    newtRefVar  result;
939
940    // verify that we have a correct lead-in
941    if (PkgReadU32(pkg->part)!=0x00001041 || PkgReadU32(pkg->part+8)!=0x00000002) {
942#       ifdef DEBUG_PKG
943        printf("*** PkgReader: PkgReadPart - unsupported NOS Part intro at %d\n",
944            pkg->part-pkg->data);
945#       endif
946        return kNewtRefNIL;
947    }
948   
949    // create an array that holds a ref to all created objects, avoiding double instantiation
950    pkg->instances = NewtMakeArray(kNewtRefUnbind, ntohl(pkg->part_header->size)/4);
951
952    // now recursively load all objects
953    p_obj = PkgReadU32(pkg->part+12);
954    result = PkgReadObject(pkg, p_obj&~3);
955
956    // release our helper array
957    NewtSetLength(pkg->instances, 0);
958
959    return result;
960}
961
962/*------------------------------------------------------------------------*/
963/** Read a part from the package file.
964 *
965 * @param pkg       [inout] the package
966 * @param index     [in] the index of the part to read starting at 0
967 *
968 * @retval  Newt object with contents of part
969 */
970newtRef PkgReadPart(pkg_stream_t *pkg, int32_t index)