diff options
Diffstat (limited to 'SrcShared/EmFileImport.cpp')
-rw-r--r-- | SrcShared/EmFileImport.cpp | 1823 |
1 files changed, 1823 insertions, 0 deletions
diff --git a/SrcShared/EmFileImport.cpp b/SrcShared/EmFileImport.cpp new file mode 100644 index 0000000..2f514e7 --- /dev/null +++ b/SrcShared/EmFileImport.cpp @@ -0,0 +1,1823 @@ +/* -*- mode: C++; tab-width: 4 -*- */ +/* ===================================================================== *\ + Copyright (c) 2000-2001 Palm, Inc. or its subsidiaries. + All rights reserved. + + This file is part of the Palm OS Emulator. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +\* ===================================================================== */ + +#include "EmCommon.h" +#include "EmFileImport.h" + +#include "EmCPU.h" // Emulator::ExecuteUntilIdle +#include "EmDlg.h" // EmDlg::DoCommonDialog +#include "EmErrCodes.h" // kError_OutOfMemory, ConvertFromPalmError, etc. +#include "EmExgMgr.h" // EmExgMgrStream, EmExgMgrImport +#include "EmLowMem.h" // TrapExists +#include "EmPalmStructs.h" // SysLibTblEntryType, RecordEntryType, RsrcEntryType, etc. +#include "EmPatchState.h" // EmPatchState::AutoAcceptBeamDialogs +#include "EmSession.h" // ExecuteUntilIdle, gSession +#include "EmStreamFile.h" // EmStreamFile, kOpenExistingForRead +#include "ErrorHandling.h" // Errors::SetParameter +#include "Miscellaneous.h" // StMemoryMapper +#include "Platform.h" // Platform::DisposeMemory +#include "ROMStubs.h" // ExgLibControl, DmFindDatabase, EvtEnqueueKey, EvtWakeup, DmDeleteDatabase, DmCreateDatabase... +#include "Strings.r.h" // kStr_CmdInstall + +#include <algorithm> // find +#include <stdio.h> // sprintf +#include <time.h> // strftime + + +/* + This module is responsible for installing files containing Palm OS + applications, databases, query applications, text data, vCals, and + vCards. + + The entire installation process is handled by the EmFileImport + class. You can either create one of these and continually call its + Continue method until either it returns an error or its Done method + returns true. Using this approach is handy if you need to do + something else in-between calls to Continue (such as update a + progress dialog). If you don't need to do anything else (like + handle UI stuff), you could instead just call the static method + LoadPalmFileList. This method installs all the given files and + doesn't return until either an error occurs or the job is done. + + Files are installed by one of two methods: either by using the + Exchange Manager, or by using low-level Database Manager routines. + The Exchange Manager is used when possible, and is the only way + that all supported file types can be installed. If the Exchange + Manager cannot be used (for example, we're emulating a Palm OS 2.0 + device, or our custom Exchange Manager driver isn't installed), + only .prc, .pdb, and .pqa files can be installed. Additionally, + after the files are installed, any application that would like + to know about those files (like the Launcher) are NOT notified, + and may fail after the installation process. Therefore, we always + try to use the Exchange Manager if possible. + + In order to use the Exchange Manager, we have to install and make + use of a small, custom driver library. This library is closely + integrated with Poser. In order to kick of an install process, + Poser makes an ExgLibControl call to our library. The library + then takes over, making the appropriate HostControl calls to + read in the contents of files, to update Poser's progress + indicator, and to report on the status of the install. Poser + merely sits back and updates the progress bar until the driver + says it's done. + + If we're not using the Exchange Manager method, we instead use + standard Database Manager calls to install the file. That is, + we call DmCreateDatabase, DmNewResource, DmWrite, etc., in + order to convert the file into a database. +*/ + +const int kInstallStart = -1; +const int kInstallMiddle = -2; +const int kInstallEnd = -3; +const int kInstallDone = -4; + +#define irGotDataChr 0x01FC // to initiate NotifyReceive + +const char kHostExgLibName[] = "HostExgLib"; +const UInt32 kHostExgLibCreator = 'HExg'; + + // Forward declarations. +extern const uint8 kHostExgLib[]; +extern const int kHostExgLibSize; + +static Bool PrvDetermineMethod (EmFileImportMethod method); +static Bool PrvCanUseExgMgr (void); +static Bool PrvHasExgMgr (void); +//static Bool PrvHostExgLibLoaded (void); +static Bool PrvIsResources (UInt16 attributes); +static Bool PrvIsResources (const EmAliasDatabaseHdrType<LAS>& hdr); +static UInt32 PrvGetEntrySize (const EmAliasDatabaseHdrType<LAS>& hdr); +static void PrvSetResourceTypeIDParameters (UInt32 resType, UInt16 resID); + +static ErrCode PrvValidateDatabase (const EmAliasDatabaseHdrType<LAS>& hdr, UInt32 size); +static void PrvSetDate (const char* varName, uint32 seconds); +static void PrvSetExgMgr (void* mgr); + +static UInt16 gHostExgLibRefNum; + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::EmFileImport + * + * DESCRIPTION: Constructor. Initializes our data members. + * + * PARAMETERS: file - ref to file to install + * gotoWhenDone - if true, switch to the application that + * just received the file we installed and display it, + * if possible. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +EmFileImport::EmFileImport (EmStream& stream, EmFileImportMethod method) : + fUsingExgMgr (::PrvDetermineMethod (method)), + fGotoWhenDone (false), + fState (kInstallStart), + fError (errNone), + fStream (stream), + + fExgMgrStream (NULL), + fExgMgrImport (NULL), + fOldAcceptBeamState (EmPatchState::AutoAcceptBeamDialogs (true)), + + fFileBuffer (NULL), + fFileBufferSize (0), + fDBID (0), + fCardNo (0), + fOpenID (0), + fCurrentEntry (0) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::~EmFileImport + * + * DESCRIPTION: Destructor. Clean up. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +EmFileImport::~EmFileImport (void) +{ + ::PrvSetExgMgr (NULL); + + delete fExgMgrStream; + delete fExgMgrImport; + + Platform::DisposeMemory (fFileBuffer); + + EmPatchState::AutoAcceptBeamDialogs (fOldAcceptBeamState); +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::LoadPalmFile + * + * DESCRIPTION: Utility function to install a Palm application, given + * a buffer containing it. + * + * PARAMETERS: data - pointer to the image. + * size - number of bytes in the image + * + * RETURNED: result code + * + ***********************************************************************/ + +ErrCode EmFileImport::LoadPalmFile (const void* data, uint32 size, + EmFileImportMethod method, + LocalID &newID) +{ + ErrCode err = errNone; + EmStreamBlock stream ((void*) data, size); + EmFileImport importer (stream, method); + + while (!importer.Done () && err == errNone) + { + err = importer.Continue (); + } + + newID = importer.GetLocalID (); + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::LoadPalmFileList + * + * DESCRIPTION: Utility function to install a collection of files. This + * is intended to be a faceless function; no UI is involved. + * + * PARAMETERS: fileList - collection of files to install. + * + * RETURNED: result code + * + ***********************************************************************/ + +ErrCode EmFileImport::LoadPalmFileList (const EmFileRefList& fileList, + EmFileImportMethod method, + vector<LocalID>& newIDList) +{ + ErrCode err = errNone; + + newIDList.clear (); + + EmFileRefList::const_iterator iter = fileList.begin (); + while (iter != fileList.end ()) + { + EmStreamFile stream (*iter, kOpenExistingForRead); + EmFileImport importer (stream, method); + + while (!importer.Done () && err == errNone) + { + err = importer.Continue (); + } + + newIDList.push_back (importer.GetLocalID ()); + + ++iter; + } + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::InstallExgMgrLib + * + * DESCRIPTION: Install our Exchange Manager stub driver library if it + * looks OK to do so. We install it if we're running + * OS 3.0 or later, and if the library is not already + * installed. + * + * PARAMETERS: None. + * + * RETURNED: result code + * + ***********************************************************************/ + +ErrCode EmFileImport::InstallExgMgrLib (void) +{ + // Don't install it if we don't have the Exchange Manager. + + if (!::PrvHasExgMgr ()) + return errNone; + + // Don't install it if it's already there. + + LocalID id = ::DmFindDatabase (0, kHostExgLibName); + if (id != 0) + return errNone; + + // OK to install it. + + return EmFileImport::LoadPalmFile (kHostExgLib, kHostExgLibSize, kMethodHomebrew, id); +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::CanUseExgMgr + * + * DESCRIPTION: Return whether or not it looks like we will use the + * Exchange Manager when installing files. Knowing this + * can be important upstream client who need to know + * what sorts of files to pass on to EmFileImport. + * + * PARAMETERS: None. + * + * RETURNED: True if we'll be using the ExgMgr. + * + ***********************************************************************/ + +Bool EmFileImport::CanUseExgMgr (void) +{ + return ::PrvCanUseExgMgr (); +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::Continue + * + * DESCRIPTION: Install a small part of the application this object has + * been charged with installing. Clients should continually + * call this method until either it returns a non-zero + * result or the Done method returns true. + * + * PARAMETERS: None. + * + * RETURNED: result code + * + ***********************************************************************/ + +ErrCode EmFileImport::Continue (void) +{ + // Install as much as we can in the allotted amount of time. + + const uint32 kIntervalTimeMSecs = 100; + uint32 start = Platform::GetMilliseconds (); + + while (fState != kInstallDone && + (Platform::GetMilliseconds () - start) < kIntervalTimeMSecs) + { + this->IncrementalInstall (); + + if (fError != errNone) + { + fState = kInstallDone; + } + } + + return fError; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::Cancel + * + * DESCRIPTION: Clean up any partially installed file. + * + * PARAMETERS: None. + * + * RETURNED: result code + * + ***********************************************************************/ + +ErrCode EmFileImport::Cancel (void) +{ + if (fUsingExgMgr) + { + this->ExgMgrInstallCancel (); + } + else + { + this->HomeBrewInstallCancel (); + } + + fState = kInstallDone; + + return fError; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::Done + * + * DESCRIPTION: Returns whether or not the file this object has been + * charged with installing has been fully installed. + * + * PARAMETERS: None. + * + * RETURNED: True if so. + * + ***********************************************************************/ + +Bool EmFileImport::Done (void) +{ + return fState == kInstallDone; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::GetProgress + * + * DESCRIPTION: Return a value indicating how far through the install + * process we currently are. + * + * PARAMETERS: None. + * + * RETURNED: A value ranging from 0 to the value returned from + * CalculateProgressMax if it were fed a list consisting + * solely of the file we're installing now. + * + ***********************************************************************/ + +long EmFileImport::GetProgress (void) +{ + if (fUsingExgMgr) + { + return fStream.GetMarker (); + } + else + { + return fCurrentEntry; + } +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::GetLocalID + * + * DESCRIPTION: Return the LocalID of the database we just installed. + * This is only valid when the importer is "Done". + * + * PARAMETERS: None. + * + * RETURNED: -1 if no database has been installed, otherwise a valid + * LocalID. + * + ***********************************************************************/ + +LocalID EmFileImport::GetLocalID (void) +{ + if (fUsingExgMgr) + { + return 0; + } + else + { + return fDBID; + } +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::CalculateProgressMax + * + * DESCRIPTION: Generate a value that can be used as a progress + * indicator's maximum value. Note that the actual value + * returned is different depending on whether the Exchange + * or Database Manager is used, so don't try interpreting + * the result too closely. + * + * PARAMETERS: None. + * + * RETURNED: A progress indicator's maximum value. + * + ***********************************************************************/ + +long EmFileImport::CalculateProgressMax (const EmFileRefList& files, EmFileImportMethod method) +{ + long result = 0; + Bool useExgMgr = ::PrvDetermineMethod (method); + + EmFileRefList::const_iterator iter = files.begin (); + while (iter != files.end ()) + { + EmStreamFile file (*iter, kOpenExistingForRead); + + if (useExgMgr) + { + // Add up all the file sizes. + + result += file.GetLength (); + } + else + { + // Add up all the number of entries. + + EmProxyDatabaseHdrType hdr; + file.GetBytes (hdr.GetPtr (), hdr.GetSize ()); + result += hdr.recordList.numRecords; + } + + ++iter; + } + + return result; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::SetResult + * + * DESCRIPTION: Called by external clients (notable the HostControl + * function called by our Exchange Manager driver) to set + * a result value. + * + * PARAMETERS: err - result of the install process. Can be errNone + * if no errors occurred. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::SetResult (Err err) +{ + this->SetResult (::ConvertFromPalmError (err)); +} + + +void EmFileImport::SetResult (ErrCode err) +{ + fError = err; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::SetDone + * + * DESCRIPTION: Called by external clients (notable the HostControl + * function called by our Exchange Manager driver) to set + * a result value. + * + * PARAMETERS: err - result of the install process. Can be errNone + * if no errors occurred. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::SetDone (void) +{ + fState = kInstallDone; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::IncrementalInstall + * + * DESCRIPTION: Install a small part of the file. Examines the current + * installation method and state and calls a function + * appropriate for the next stage. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::IncrementalInstall (void) +{ + if (fUsingExgMgr) + { + switch (fState) + { + case kInstallStart: this->ExgMgrInstallStart (); break; + case kInstallMiddle: this->ExgMgrInstallMiddle (); break; + case kInstallEnd: this->ExgMgrInstallEnd (); break; + case kInstallDone: break; + } + } + else + { + switch (fState) + { + case kInstallStart: this->HomeBrewInstallStart (); break; + case kInstallMiddle: this->HomeBrewInstallMiddle (); break; + case kInstallEnd: this->HomeBrewInstallEnd (); break; + case kInstallDone: break; + } + } +} + + +#pragma mark - + +/*********************************************************************** + * + * FUNCTION: EmFileImport::ExgMgrInstallStart + * + * DESCRIPTION: Performs the first part of the installation process + * using the Exchange Manager. This stage involves telling + * our driver what file to install, and then telling it + * to start installing. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::ExgMgrInstallStart (void) +{ + this->ValidateStream (); + if (fError) + return; + + // Create the stub ExgMgr driver callback object. + + EmAssert (gHostExgLibRefNum != 0); + EmAssert (fExgMgrStream == NULL); + EmAssert (fExgMgrImport == NULL); + + fStream.SetMarker (0, kStreamFromStart); + fExgMgrStream = new EmExgMgrStream (fStream); + fExgMgrImport = new EmExgMgrImportWrapper (*fExgMgrStream, *this); + + // Tell the library what host-based driver to use. + + ::PrvSetExgMgr (fExgMgrImport); + + if (!fError) + { + // Kick off the install process. + + Err err = ::EvtEnqueueKey (irGotDataChr, gHostExgLibRefNum, libEvtHookKeyMask); + this->SetResult (err); + + err = ::EvtWakeup (); + } + + fState = kInstallMiddle; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::ExgMgrInstallMiddle + * + * DESCRIPTION: Performs any repeating part of the installation process + * using the Exchange Manager. This stage merely involves + * giving the Emulator a chance to execute. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::ExgMgrInstallMiddle (void) +{ + EmAssert (gSession); +// !!! gSession->ExecuteUntilIdle (); +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::ExgMgrInstallEnd + * + * DESCRIPTION: Performs any final part of the installation process + * using the Exchange Manager. There's nothing we need + * to do here. The end of the installation process is + * signalled when our Exchange Manager driver calls + * SetResult via the HostControl functions. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::ExgMgrInstallEnd (void) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::ExgMgrInstallCancel + * + * DESCRIPTION: Cancels the installation process using the Exchange + * Manager. We do this by sending a message to our + * Exchange Manager driver. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::ExgMgrInstallCancel (void) +{ + // Tell the library to stop. + + fExgMgrImport->Cancel (); +} + + +#pragma mark - + +/*********************************************************************** + * + * FUNCTION: EmFileImport::HomeBrewInstallStart + * + * DESCRIPTION: Performs the first part of the installation process + * using the Database Manager. This stage involves + * loading the file, scoping out its header, deleting any + * old databases with the same name, creating the new + * database, setting its attributes, and opening it. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::HomeBrewInstallStart (void) +{ + this->ValidateStream (); + if (fError) + return; + + Err err = errNone; + + // Assume failure + + fCardNo = 0; + fDBID = 0; + fOpenID = 0; + + // Get the file and read it into memory. + // Set up a proxy to get to the header contents. + + EmAssert (fFileBuffer == NULL); + + fFileBufferSize = fStream.GetLength (); + fFileBuffer = Platform::AllocateMemory (fFileBufferSize); + if (!fFileBuffer) + { + this->SetResult (kError_OutOfMemory); + return; + } + + fStream.SetMarker (0, kStreamFromStart); + fStream.GetBytes (fFileBuffer, fFileBufferSize); + + EmAliasDatabaseHdrType<LAS> hdr (fFileBuffer); + + // See if there's already a database with this name. + // If the database already exists, delete it. + + LocalID tempID = ::DmFindDatabase (fCardNo, (Char*) hdr.name.GetPtr ()); + + if (tempID != 0) + { + err = ::DmDeleteDatabase (fCardNo, tempID); + + if (err && err != dmErrROMBased) + goto Error; + } + + // Create the new database + + err = ::DmCreateDatabase (fCardNo, (Char*) hdr.name.GetPtr (), + hdr.creator, hdr.type, ::PrvIsResources (hdr)); + if (err) + goto Error; + + // Set the database info + + fDBID = ::DmFindDatabase (fCardNo, (Char*) hdr.name.GetPtr ()); + if (!fDBID) + { + err = ::DmGetLastErr (); + goto Error; + } + + { + UInt16 attributes = hdr.attributes; + UInt16 version = hdr.version; + UInt32 creationDate = hdr.creationDate; + UInt32 modificationDate = hdr.modificationDate; + UInt32 lastBackupDate = hdr.lastBackupDate; + UInt32 modificationNumber = hdr.modificationNumber; + UInt32 type = hdr.type; + UInt32 creator = hdr.creator; + + err = ::DmSetDatabaseInfo (fCardNo, fDBID, NULL, + &attributes, &version, &creationDate, + &modificationDate, &lastBackupDate, + &modificationNumber, NULL, + NULL, &type, &creator); + + if (err) + goto Error; + } + + // Open the new database + + fOpenID = ::DmOpenDatabase (fCardNo, fDBID, dmModeReadWrite); + if (!fOpenID) + { + err = ::DmGetLastErr (); + goto Error; + } + + // If this is a record database and there's an appInfo block, add it. + + if (!::PrvIsResources (hdr)) + { + if (hdr.appInfoID) + { + UInt32 recSize; + MemHandle newRecH; + + EmAliasRecordEntryType<LAS> recordEntry (hdr.recordList.records); + + if (hdr.sortInfoID) + { + recSize = (UInt32) hdr.sortInfoID - (UInt32) hdr.appInfoID; + } + else if (hdr.recordList.numRecords > 0) + { + recSize = (UInt32) recordEntry.localChunkID - (UInt32) hdr.appInfoID; + } + else + { + recSize = fFileBufferSize - (UInt32) hdr.appInfoID; + } + + // Set the error parameters in case an error occurs. + + Errors::SetParameter ("%res_size", recSize); + + // Allocate the new record. + + newRecH = (MemHandle) ::DmNewHandle (fOpenID, recSize); + if (!newRecH) + { + err = ::DmGetLastErr (); + goto Error; + } + + // Copy the data in. + + UInt8* srcP = (UInt8*) hdr.GetPtr () + (UInt32) hdr.appInfoID; + UInt8* dstP = (UInt8*) ::MemHandleLock ((MemHandle) newRecH); + ::DmWrite (dstP, 0, srcP, recSize); + ::MemHandleUnlock ((MemHandle) newRecH); + + // Store it in the header. + + LocalID appInfoID = ::MemHandleToLocalID ((MemHandle) newRecH); + err = ::DmSetDatabaseInfo (fCardNo, fDBID, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, &appInfoID, NULL, NULL, NULL); + + if (err) + goto Error; + } + } + + // If there are resources/records, start installing them. + + if (hdr.recordList.numRecords > 0) + fState = kInstallMiddle; + else + fState = kInstallEnd; + + return; + +Error: + this->DeleteCurrentDatabase (); + this->SetResult (err); +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::HomeBrewInstallMiddle + * + * DESCRIPTION: Performs any repeating part of the installation process + * using the Exchange Manager. This stage involves + * creating and installing the next resource or record + * in the file. If this is a record database and this + * is the first call to this method, we also install + * the appInfo record. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::HomeBrewInstallMiddle (void) +{ + Err err = errNone; + + UInt8* srcP; + UInt8* dstP; + + EmAssert (fFileBuffer); + EmAliasDatabaseHdrType<LAS> hdr (fFileBuffer); + + //------------------------------------------------------------ + // If it's a resource database, add the resources + //------------------------------------------------------------ + + if (::PrvIsResources (hdr)) + { + UInt32 resSize; + MemHandle newResH; + + EmAliasRsrcEntryType<LAS> rsrcEntry (hdr.recordList.resources[fCurrentEntry]); + + // Set the error parameters in case an error occurs. + + Errors::SetParameter ("%record_number", fCurrentEntry); + ::PrvSetResourceTypeIDParameters (rsrcEntry.type, rsrcEntry.id); + + // Calculate the size of the resource. + + if (fCurrentEntry < hdr.recordList.numRecords - 1) + { + resSize = (UInt32) rsrcEntry[1].localChunkID; + resSize -= (UInt32) rsrcEntry.localChunkID; + } + else + { + resSize = fFileBufferSize - (UInt32) rsrcEntry.localChunkID; + } + + // Set the error parameters in case an error occurs. + + Errors::SetParameter ("%res_size", resSize); + + // Allocate the new resource. + + newResH = (MemHandle) ::DmNewResource (fOpenID, rsrcEntry.type, rsrcEntry.id, resSize); + if (!newResH) + { + err = ::DmGetLastErr (); + if (err == dmErrMemError) + { + this->DeleteCurrentDatabase (); + this->SetResult (kError_BadDB_ResourceMemError); + return; + } + goto Error; + } + + // Copy the data in. + + srcP = (UInt8*) hdr.GetPtr () + (UInt32) rsrcEntry.localChunkID; + dstP = (UInt8*) ::MemHandleLock ((MemHandle) newResH); + ::DmWrite (dstP, 0, srcP, resSize); + ::MemHandleUnlock ((MemHandle) newResH); + + // Done with it. + + ::DmReleaseResource ((MemHandle) newResH); + } + + //------------------------------------------------------------ + // If it's a record database, add the records + //------------------------------------------------------------ + + else + { + UInt32 recSize; + MemHandle newRecH; + + EmAliasRecordEntryType<LAS> recordEntry (hdr.recordList.records[fCurrentEntry]); + + // Set the error parameters in case an error occurs. + + Errors::SetParameter ("%record_number", fCurrentEntry); + + // Calculate the size of the record. + + if (fCurrentEntry < hdr.recordList.numRecords - 1) + { + recSize = (UInt32) recordEntry[1].localChunkID; + recSize -= (UInt32) recordEntry.localChunkID; + } + else + { + recSize = fFileBufferSize - (UInt32) recordEntry.localChunkID; + } + + // Set the error parameters in case an error occurs. + + Errors::SetParameter ("%res_size", recSize); + + // Only add non-nil records. When the default databases are created + // they sometimes have nil records if the user had to delete a + // record they didn't want. + + if (recSize) + { + // Allocate the new record. + + UInt16 index = 0xFFFF; + newRecH = (MemHandle) ::DmNewRecord (fOpenID, &index, recSize); + if (!newRecH) + { + err = ::DmGetLastErr (); + if (err == dmErrMemError) + { + this->DeleteCurrentDatabase (); + this->SetResult (kError_BadDB_RecordMemError); + return; + } + goto Error; + } + + // Copy the data in. + + srcP = (UInt8*) hdr.GetPtr () + (UInt32) recordEntry.localChunkID; + dstP = (UInt8*) ::MemHandleLock ((MemHandle) newRecH); + ::DmWrite (dstP, 0, srcP, recSize); + ::MemHandleUnlock ((MemHandle) newRecH); + + // Set the attributes. + + UInt16 attr = recordEntry.attributes; + UInt32 id = recordEntry.uniqueID[0]; + id = (id << 8) | recordEntry.uniqueID[1]; + id = (id << 8) | recordEntry.uniqueID[2]; + err = ::DmSetRecordInfo (fOpenID, index, &attr, &id); + if (err) + goto Error; + + // Done with it. + + ::DmReleaseRecord (fOpenID, index, true); + } + } + + fCurrentEntry++; + + if (fCurrentEntry >= hdr.recordList.numRecords) + { + fState = kInstallEnd; + } + + return; + +Error: + this->DeleteCurrentDatabase (); + this->SetResult (err); +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::HomeBrewInstallEnd + * + * DESCRIPTION: Performs any final part of the installation process + * using the Exchange Manager. This stage involves + * setting some final information about the database and + * then closing it up. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::HomeBrewInstallEnd (void) +{ + EmAssert (fFileBuffer); + EmAliasDatabaseHdrType<LAS> hdr (fFileBuffer); + + // Fixup the modification # to match what was in the image. + + EmAssert (fDBID); + UInt32 modificationNumber = hdr.modificationNumber; + Err err = ::DmSetDatabaseInfo (fCardNo, fDBID, NULL, NULL, NULL, NULL, + NULL, NULL, &modificationNumber, NULL, NULL, NULL, NULL); + + if (err) + { + this->SetResult (err); + return; + } + + // Close up and drop our references. + + EmAssert (fOpenID); + ::DmCloseDatabase (fOpenID); + + fOpenID = 0; +// fDBID = 0; // Don't zap this, we return it in GetLocalID. + + fState = kInstallDone; +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::HomeBrewInstallCancel + * + * DESCRIPTION: Cancels the installation process using the Database + * Manager. This stage involves merely deleting the + * database we were in the middle of creating. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::HomeBrewInstallCancel (void) +{ + this->DeleteCurrentDatabase (); +} + + +#pragma mark - + +/*********************************************************************** + * + * FUNCTION: EmFileImport::ValidateStream + * + * DESCRIPTION: Preflight the installation of certain files. If the + * stream contains a .prc, .pdb, or .pqa file, then make + * sure the contents are valid. Otherwise, the OS might + * discover the error and call ErrDisplay. We can't + * recover from that right now, so try to make sure it + * doesn't happen. + * + * Note: in general, the EmFileImport class doesn't care + * what kind of stream it's using for input. The stream + * could be a disk file, a memory buffer, or even a TCP + * socket. For the most part, we don't care. However, + * *this* function does. In order to help determine + * what the stream contains (one of the files we can + * validate, or something else, like a text, vCard, or + * vCal file), we look at the file type. Therefore, + * we try casting the base stream into an EmStreamFile. + * If that works, then we can check the file type. If + * the cast fails, then we just assume the best and + * say the file can be installed. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::ValidateStream (void) +{ + // Only validate resource/record files. Which, in turn, means + // that we can only validate EmStreamFiles. Anything else, well, + // we hope they're OK. + + EmStreamFile* fileStream = dynamic_cast <EmStreamFile*> (&fStream); + if (fileStream == NULL) + return; + + EmFileRef fileRef = fileStream->GetFileRef (); + if (fileRef.IsType (kFileTypePalmApp) || + fileRef.IsType (kFileTypePalmDB) || + fileRef.IsType (kFileTypePalmQA)) + { + long len = fStream.GetLength (); + StMemory buffer (len); + void* bufferP = buffer.Get (); + + fStream.SetMarker (0, kStreamFromStart); + fStream.GetBytes (bufferP, len); + + EmAliasDatabaseHdrType<LAS> hdr (bufferP); + + ErrCode err = ::PrvValidateDatabase (hdr, len); + this->SetResult (err); + } + + // If we're installing something other than a resource/record + // file, then we can only do that if we're using the Exchange + // Manager + + else + { + if (!fUsingExgMgr) + { + this->SetResult (kError_UnknownType); + } + } +} + + +/*********************************************************************** + * + * FUNCTION: EmFileImport::DeleteCurrentDatabase + * + * DESCRIPTION: Delete the database currently being installed. + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void EmFileImport::DeleteCurrentDatabase (void) +{ + if (fOpenID) + { + ::DmCloseDatabase (fOpenID); + fOpenID = 0; + } + + if (fDBID) + { + ::DmDeleteDatabase (fCardNo, fDBID); + fDBID = 0; + } +} + + +#pragma mark - + +/*********************************************************************** + * + * FUNCTION: PrvDetermineMethod + * + * DESCRIPTION: Return whether or not we should use the Exchange + * Manager for installing the file. Right now, we check + * only for existance of the Exchange Manager and whether + * or not our library is installed. In the future, we may + * also check for valid file types and user preference + * settings. + * + * PARAMETERS: None. + * + * RETURNED: True if we'll be using the ExgMgr. + * + ***********************************************************************/ + +Bool PrvDetermineMethod (EmFileImportMethod method) +{ + return + (method == kMethodExgMgr) ? true : + (method == kMethodHomebrew) ? false : + ::PrvCanUseExgMgr (); +} + + +/*********************************************************************** + * + * FUNCTION: PrvCanUseExgMgr + * + * DESCRIPTION: Return whether or not we should use the Exchange + * Manager for installing the file. Right now, we check + * only for existance of the Exchange Manager and whether + * or not our library is installed. In the future, we may + * also check for valid file types and user preference + * settings. + * + * PARAMETERS: None. + * + * RETURNED: True if we'll be using the ExgMgr. + * + ***********************************************************************/ + +Bool PrvCanUseExgMgr (void) +{ +#if 0 + return ::PrvHasExgMgr () && ::PrvHostExgLibLoaded (); +#else + return false; +#endif +} + + +/*********************************************************************** + * + * FUNCTION: PrvHasExgMgr + * + * DESCRIPTION: Return whether or not the Exchange Manager is installed. + * + * PARAMETERS: None. + * + * RETURNED: True if it is. + * + ***********************************************************************/ + +Bool PrvHasExgMgr (void) +{ + return EmLowMem::TrapExists (sysTrapExgInit); +} + + +/*********************************************************************** + * + * FUNCTION: PrvHostExgLibLoaded + * + * DESCRIPTION: . + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +#if 0 +Bool PrvHostExgLibLoaded (void) +{ + // Try finding the library. + UInt16 libRefNum; + Err err = ::SysLibFind (kHostExgLibName, &libRefNum); + + // If we can't find it, try loading it. + if (err == sysErrLibNotFound) + err = ::SysLibLoad (sysFileTExgLib, kHostExgLibCreator, &libRefNum); + + if (!err) + { + gHostExgLibRefNum = libRefNum; + return true; + } + + return false; +} +#endif + + +/*********************************************************************** + * + * FUNCTION: PrvIsResources + * + * DESCRIPTION: . + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +Bool PrvIsResources (UInt16 attributes) +{ + return (attributes & dmHdrAttrResDB); +} + + +/*********************************************************************** + * + * FUNCTION: PrvIsResources + * + * DESCRIPTION: . + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +Bool PrvIsResources (const EmAliasDatabaseHdrType<LAS>& hdr) +{ + return ::PrvIsResources (hdr.attributes); +} + + +/*********************************************************************** + * + * FUNCTION: PrvGetEntrySize + * + * DESCRIPTION: . + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +UInt32 PrvGetEntrySize (const EmAliasDatabaseHdrType<LAS>& hdr) +{ + if (::PrvIsResources (hdr)) + return EmAliasRsrcEntryType<LAS>::GetSize (); + + return EmAliasRecordEntryType<LAS>::GetSize (); +} + + +/*********************************************************************** + * + * FUNCTION: PrvSetResourceTypeIDParameters + * + * DESCRIPTION: . + * + * PARAMETERS: None. + * + * RETURNED: Nothing. + * + ***********************************************************************/ + +void PrvSetResourceTypeIDParameters (UInt32 resType, UInt16 resID) +{ + char buffer[20]; + + sprintf (buffer, "%c%c%c%c", + (char) (resType >> 24), + (char) (resType >> 16), + (char) (resType >> 8), + (char) (resType >> 0)); + Errors::SetParameter ("%res_type", buffer); + + Errors::SetParameter ("%res_id", resID); +} + + +/*********************************************************************** + * + * FUNCTION: PrvValidateDatabaseHeader + * + * DESCRIPTION: Validates database header (to be used before any of the + * rest of the database is byteswapped or otherwise + * processed). + * + * PARAMETERS: hdrP - pointer to database header + * bufferSize - size of the buffer holding the database + * + * RETURNED: Err - error code + * + ***********************************************************************/ + +static ErrCode PrvValidateDatabaseHeader(const EmAliasDatabaseHdrType<LAS>& hdr, UInt32 bufferSize) +{ + // Make sure the database name is not too long. + + char* namePtr = (char*) hdr.name.GetPtr (); + size_t len = strlen (namePtr); + + if (len >= dmDBNameLength) + { + char databaseName[dmDBNameLength]; + memcpy (databaseName, namePtr, dmDBNameLength); + + databaseName[dmDBNameLength - 4] = '.'; + databaseName[dmDBNameLength - 3] = '.'; + databaseName[dmDBNameLength - 2] = '.'; + databaseName[dmDBNameLength - 1] = '\0'; + + Errors::SetParameter ("%database_name", databaseName); + return kError_BadDB_NameNotNULLTerminated; + } + + // Check to see that name consists of printable characters + + for (size_t ii = 0; ii < len; ++ii) + { + uint8 ch = (uint8) namePtr[ii]; + + if (ch < ' ' || ch > 0x7E) + { + Errors::SetParameter ("%database_name", namePtr); + return kError_BadDB_NameNotPrintable; + } + } + + // Check that the modification date is not older than the creation date. + // We don't care about this and neither does the Palm OS, but HotSync does. + + uint32 creationDate = hdr.creationDate; + uint32 modificationDate = hdr.modificationDate; + + if (creationDate > modificationDate) + { + // Just show the problem here, but don't consider it fatal. + ::PrvSetDate ("%cr_date", hdr.creationDate); + ::PrvSetDate ("%mod_date", hdr.modificationDate); + Errors::SetParameter ("%database_name", namePtr); + string msg = Errors::ReplaceParameters (kStr_InconsistentDatabaseDates); + EmDlg::DoCommonDialog (msg, kDlgFlags_OK); + } + + // Also check for zero creation dates. + // We don't care about this and neither does the Palm OS, but HotSync does. + + else if (creationDate == 0) + { + // Just show the problem here, but don't consider it fatal. + Errors::SetParameter ("%database_name", namePtr); + string msg = Errors::ReplaceParameters (kStr_NULLDatabaseDate); + EmDlg::DoCommonDialog (msg, kDlgFlags_OK); + } + + // Check that the resource/database records headers fit within the space + // of the buffer. + + UInt32 endOfEntryArray = EmAliasDatabaseHdrType<LAS>::GetSize () + + ::PrvGetEntrySize (hdr) * hdr.recordList.numRecords; + + if (endOfEntryArray > bufferSize) + return kError_BadDB_FileTooSmall; + + // If we got here, we're okay. + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: PrvValidateEntries + * + * DESCRIPTION: Validates entries in a database + * + * PARAMETERS: hdrP - pointer to database header + * bufferSize - size of the buffer holding the database + * + * RETURND: Err - error code + * + ***********************************************************************/ + +static ErrCode PrvValidateEntries(const EmAliasDatabaseHdrType<LAS>& hdr, UInt32 bufferSize) +{ + Bool isResource = ::PrvIsResources (hdr); + + // Establish the upper and lower ranges for localChunkIDs. These IDs are + // offsets from the beginning of the buffer/file, so they can't be larger + // than the size of the buffer/file, nor can they be smaller than the + // starting offset of the chunks. + + UInt32 upperRange = bufferSize; + UInt32 lowerRange = EmAliasDatabaseHdrType<LAS>::GetSize () + + ::PrvGetEntrySize (hdr) * hdr.recordList.numRecords; + + // Create an array to keep track of the discovered resource type/ids. + // This is so we can find duplicate entries. + + typedef pair<UInt32, UInt16> ResTypeIDPair; + vector<ResTypeIDPair> resPairs; + + // nextRecordListID not supported for now. + + if (hdr.recordList.nextRecordListID != 0) + return kError_BadDB_nextRecordListIDNonZero; + + for (UInt16 recordNumber = 0; recordNumber < hdr.recordList.numRecords; ++recordNumber) + { + LocalID itsLocalID; + long itsSize; // Use a signed value to detect decreasing LocalIDs + + // Check for negative sizes and get the LocalID. + // --------------------------------------------- + // Though the code is similar for resources and for records, we have separate + // blocks so that we can safely cast to different types to access the localChunkID + // field. + + if (isResource) + { + EmAliasRsrcEntryType<LAS> rsrcEntry (hdr.recordList.resources[recordNumber]); + + // Set the error parameters in case an error occurs. + + ::PrvSetResourceTypeIDParameters (rsrcEntry.type, rsrcEntry.id); + + // Check for duplicate resources. + + ResTypeIDPair thisPair (rsrcEntry.type, rsrcEntry.id); + + if (find (resPairs.begin (), resPairs.end (), thisPair) != resPairs.end ()) + return kError_BadDB_DuplicateResource; + + resPairs.push_back (thisPair); + + // Get the size of the resource. + + if (recordNumber < hdr.recordList.numRecords - 1) + { + itsSize = (UInt32) rsrcEntry[1].localChunkID; + itsSize -= (UInt32) rsrcEntry.localChunkID; + } + else + itsSize = bufferSize - (UInt32) rsrcEntry.localChunkID; + + // Set the error parameters in case an error occurs. + + Errors::SetParameter ("%res_size", itsSize); + + // Check for negative sizes (zero is NOT okay for resources). + + if (itsSize <= 0) + return kError_BadDB_ResourceTooSmall; + + itsLocalID = rsrcEntry.localChunkID; + } + else + { + EmAliasRecordEntryType<LAS> recordEntry (hdr.recordList.records[recordNumber]); + + // Set the error parameters in case an error occurs. + + Errors::SetParameter ("%record_number", recordNumber); + + if (recordNumber < hdr.recordList.numRecords - 1) + { + itsSize = (UInt32) recordEntry[1].localChunkID; + itsSize -= (UInt32) recordEntry.localChunkID; + } + else + itsSize = bufferSize - (UInt32) recordEntry.localChunkID; + + // Set the error parameters in case an error occurs. + + Errors::SetParameter ("%res_size", itsSize); + + // Check for negative sizes (zero is okay for records). + + if (itsSize < 0) + return kError_BadDB_RecordTooSmall; + + itsLocalID = recordEntry.localChunkID; + } + + // Test that the beginning and ending of the chunk are in range. + + if (itsLocalID < lowerRange || itsLocalID + itsSize > upperRange) + return isResource + ? kError_BadDB_ResourceOutOfRange + : kError_BadDB_RecordOutOfRange; + } + + // If we got here, we're okay + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: PrvValidateDatabase + * + * DESCRIPTION: + * + * PARAMETERS: hdrP - pointer to database header + * bufferSize - size of the buffer holding the database + * + * RETURNED: Err - error code + * + ***********************************************************************/ + +static ErrCode PrvValidateDatabase (const EmAliasDatabaseHdrType<LAS>& hdr, UInt32 size) +{ + ErrCode err; + + // Make sure the file has enough of a header to validate. + + if (size < hdr.GetSize ()) + { + err = kError_BadDB_FileTooSmall; + goto Exit; + } + + // Validate the header. + + err = ::PrvValidateDatabaseHeader (hdr, size); + if (err != errNone) + goto Exit; + + // Check out the record entries; make sure they're okay + + err = ::PrvValidateEntries (hdr, size); + +Exit: + if (!err) + Errors::ClearAllParameters (); + + return err; +} + +void PrvSetDate (const char* varName, uint32 seconds) +{ +// this table tells you how many days since the last 'daysInFourYears' boundary. +// That is, the first 12 entries give you the days in previous months for a leap year, +// e.g. table entry 3 (March) is 60 because there are 31 days in January and 29 in February +// each successive 12 entries give you the days in previous months for that year plus the +// days in previous years. That allows you to do just one table lookup with (year % 4) + month +// to find out how many days have gone by since the start of the previous leap year. +// (See DateToDays for how this is used.) +// +// There's one extra entry at the end, so that this gives you the days in a given month: +// daysInPrevMonths[(year%4)*12+month] - daysInPrevMonth[(year%4)*12+month-1] +// +// It's OK to use just one table for all this, because over our valid date range +// (1904 - 2032) all years divisible by 4 are leap years. + +static const UInt16 daysInPrevMonths[] = +{ +// +31 +29 +31 +30 +31 +30 + 0, 31, 60, 91, 121, 152, // 1904, 1908, ... + +// +31 +31 +30 +31 +30 +31 + 182, 213, 244, 274, 305, 335, // (leap years!) + +// +31 +28 +31 +30 +31 +30 + 366+0, 366+31, 366+59, 366+90, 366+120, 366+151, // 1905, 1909, ... + +// +31 +31 +30 +31 +30 +31 + 366+181, 366+212, 366+243, 366+273, 366+304, 366+334, + + 731+0, 731+31, 731+59, 731+90, 731+120, 731+151, // 1906, 1910, ... + 731+181, 731+212, 731+243, 731+273, 731+304, 731+334, + + 1096+0, 1096+31, 1096+59, 1096+90, 1096+120, 1096+151,// 1907, 1911, ... + 1096+181, 1096+212, 1096+243, 1096+273, 1096+304, 1096+334, + + 1461 +}; + + UInt32 days; + UInt16 month; + DateTimeType dateTime; + + // Now convert from seconds to days. + days = seconds / daysInSeconds; + seconds = seconds % daysInSeconds; + dateTime.weekDay = (days + 5) % daysInWeek; + + // get year to nearest leap year + dateTime.year = firstYear + (days / daysInFourYears) * 4; + + // now figure out month/year within 4-year range + days %= daysInFourYears; + month = 0; + while (days >= daysInPrevMonths[month]) + month++; + month--; + + dateTime.year += month / monthsInYear; + dateTime.month = (month % monthsInYear) + 1; + dateTime.day = days - daysInPrevMonths[month] + 1; + + // now calc the time + dateTime.hour = seconds / hoursInSeconds; + seconds = seconds % hoursInSeconds; + dateTime.minute = seconds / minutesInSeconds; + seconds = seconds % minutesInSeconds; + dateTime.second = seconds; + + // Convert this into text. + + char buffer[200]; + struct tm time; + + time.tm_sec = dateTime.second; + time.tm_min = dateTime.minute; + time.tm_hour = dateTime.hour; + time.tm_mday = dateTime.day - 1; + time.tm_mon = dateTime.month - 1; + time.tm_year = dateTime.year - 1900; + time.tm_wday = dateTime.weekDay; + time.tm_yday = 0; + time.tm_isdst = -1; + + strftime (buffer, 200, "%B %d, %Y, %I:%M:%S %p", &time); +// strftime (buffer, 200, "%c", &time); + + Errors::SetParameter (varName, buffer); +} + + +void PrvSetExgMgr (void* mgr) +{ + if (gHostExgLibRefNum) + { + emuptr tblP = (emuptr) ::SysLibTblEntry (gHostExgLibRefNum); + if (tblP) + { + EmAliasSysLibTblEntryType<PAS> tbl (tblP); + tbl.globalsP = (emuptr) mgr; + } + } +} + +const uint8 kHostExgLib[] = +{ + 0x48, 0x6F, 0x73, 0x74, 0x45, 0x78, 0x67, 0x4C, 0x69, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, 0xB5, 0x85, 0x65, 0x02, 0xB5, 0x85, 0x65, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x78, 0x67, 0x6C, + 0x48, 0x45, 0x78, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6C, 0x69, + 0x62, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x48, 0x7A, 0x00, 0x04, 0x06, 0x97, + 0x00, 0x00, 0x00, 0x06, 0x4E, 0x75, 0x2F, 0x0A, 0x2F, 0x03, 0x59, 0x4F, 0x24, 0x6F, 0x00, 0x12, + 0x48, 0x57, 0x3F, 0x3C, 0x00, 0x01, 0x2F, 0x3C, 0x70, 0x73, 0x79, 0x73, 0x4E, 0x4F, 0xA2, 0x7B, + 0x36, 0x00, 0x4F, 0xEF, 0x00, 0x0A, 0x66, 0x08, 0x0C, 0x97, 0x03, 0x00, 0x00, 0x00, 0x64, 0x06, + 0x30, 0x3C, 0x15, 0x02, 0x60, 0x0C, 0x4E, 0xBA, 0x02, 0x16, 0x24, 0x88, 0x42, 0xAA, 0x00, 0x04, + 0x70, 0x00, 0x58, 0x4F, 0x26, 0x1F, 0x24, 0x5F, 0x4E, 0x75, 0x3F, 0x2F, 0x00, 0x04, 0x3F, 0x3C, + 0x05, 0x80, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x54, 0x4F, 0x4E, 0x75, 0x3F, 0x2F, 0x00, 0x04, + 0x3F, 0x3C, 0x05, 0x81, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x54, 0x4F, 0x4E, 0x75, 0x3F, 0x2F, + 0x00, 0x04, 0x3F, 0x3C, 0x05, 0x83, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x54, 0x4F, 0x4E, 0x75, + 0x3F, 0x2F, 0x00, 0x04, 0x3F, 0x3C, 0x05, 0x82, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x54, 0x4F, + 0x4E, 0x75, 0x2F, 0x0A, 0x2F, 0x03, 0x59, 0x4F, 0x36, 0x2F, 0x00, 0x10, 0x24, 0x6F, 0x00, 0x12, + 0x0C, 0x52, 0x00, 0x04, 0x66, 0x10, 0x30, 0x2A, 0x00, 0x0C, 0x02, 0x40, 0x04, 0x00, 0x67, 0x06, + 0xB6, 0x6A, 0x00, 0x0A, 0x67, 0x06, 0x70, 0x00, 0x60, 0x00, 0x00, 0x9C, 0x0C, 0x6A, 0x01, 0xFC, + 0x00, 0x08, 0x66, 0x00, 0x00, 0x82, 0x3E, 0xBC, 0x00, 0x3C, 0x70, 0x00, 0x30, 0x17, 0x2F, 0x00, + 0x4E, 0x4F, 0xA0, 0x13, 0x24, 0x48, 0x20, 0x0A, 0x58, 0x4F, 0x66, 0x04, 0x70, 0x00, 0x60, 0x76, + 0x42, 0x67, 0x2F, 0x0A, 0x4E, 0x4F, 0xA0, 0x1B, 0x48, 0x6F, 0x00, 0x06, 0x2F, 0x0A, 0x3F, 0x3C, + 0x80, 0x00, 0x3F, 0x03, 0x4E, 0x4F, 0xA8, 0x0D, 0x3F, 0x40, 0x00, 0x14, 0x4A, 0x6F, 0x00, 0x14, + 0x4F, 0xEF, 0x00, 0x12, 0x66, 0x0C, 0x2F, 0x0A, 0x4E, 0x4F, 0xA3, 0x10, 0x3F, 0x40, 0x00, 0x06, + 0x58, 0x4F, 0x48, 0x57, 0x2F, 0x0A, 0x3F, 0x3C, 0x80, 0x01, 0x3F, 0x03, 0x4E, 0x4F, 0xA8, 0x0D, + 0x42, 0xA7, 0x48, 0x6F, 0x00, 0x12, 0x3F, 0x3C, 0x80, 0x02, 0x3F, 0x03, 0x4E, 0x4F, 0xA8, 0x0D, + 0x2F, 0x0A, 0x4E, 0x4F, 0xA0, 0x12, 0x4A, 0x6F, 0x00, 0x1E, 0x57, 0xC0, 0x44, 0x00, 0x48, 0x80, + 0x4F, 0xEF, 0x00, 0x20, 0x60, 0x12, 0x2F, 0x0A, 0x3F, 0x03, 0x3F, 0x3C, 0x05, 0x84, 0x4E, 0x4F, + 0xA3, 0x44, 0x54, 0x4F, 0x5C, 0x4F, 0x58, 0x4F, 0x26, 0x1F, 0x24, 0x5F, 0x4E, 0x75, 0x2F, 0x2F, + 0x00, 0x06, 0x3F, 0x2F, 0x00, 0x08, 0x3F, 0x3C, 0x05, 0x85, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, + 0x5C, 0x4F, 0x4E, 0x75, 0x2F, 0x2F, 0x00, 0x06, 0x3F, 0x2F, 0x00, 0x08, 0x3F, 0x3C, 0x05, 0x86, + 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x5C, 0x4F, 0x4E, 0x75, 0x3F, 0x2F, 0x00, 0x0A, 0x2F, 0x2F, + 0x00, 0x08, 0x3F, 0x2F, 0x00, 0x0A, 0x3F, 0x3C, 0x05, 0x87, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, + 0x50, 0x4F, 0x4E, 0x75, 0x2F, 0x2F, 0x00, 0x06, 0x3F, 0x2F, 0x00, 0x08, 0x3F, 0x3C, 0x05, 0x89, + 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x5C, 0x4F, 0x4E, 0x75, 0x2F, 0x2F, 0x00, 0x06, 0x3F, 0x2F, + 0x00, 0x08, 0x3F, 0x3C, 0x05, 0x88, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x5C, 0x4F, 0x4E, 0x75, + 0x2F, 0x2F, 0x00, 0x12, 0x2F, 0x2F, 0x00, 0x12, 0x2F, 0x2F, 0x00, 0x12, 0x2F, 0x2F, 0x00, 0x12, + 0x3F, 0x2F, 0x00, 0x14, 0x3F, 0x3C, 0x05, 0x8A, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x4F, 0xEF, + 0x00, 0x12, 0x4E, 0x75, 0x2F, 0x2F, 0x00, 0x12, 0x2F, 0x2F, 0x00, 0x12, 0x2F, 0x2F, 0x00, 0x12, + 0x2F, 0x2F, 0x00, 0x12, 0x3F, 0x2F, 0x00, 0x14, 0x3F, 0x3C, 0x05, 0x8B, 0x4E, 0x4F, 0xA3, 0x44, + 0x54, 0x4F, 0x4F, 0xEF, 0x00, 0x12, 0x4E, 0x75, 0x2F, 0x2F, 0x00, 0x0C, 0x2F, 0x2F, 0x00, 0x0C, + 0x3F, 0x2F, 0x00, 0x0E, 0x3F, 0x2F, 0x00, 0x0E, 0x3F, 0x3C, 0x05, 0x8C, 0x4E, 0x4F, 0xA3, 0x44, + 0x54, 0x4F, 0x4F, 0xEF, 0x00, 0x0C, 0x4E, 0x75, 0x2F, 0x2F, 0x00, 0x06, 0x3F, 0x2F, 0x00, 0x08, + 0x3F, 0x3C, 0x05, 0x8D, 0x4E, 0x4F, 0xA3, 0x44, 0x54, 0x4F, 0x5C, 0x4F, 0x4E, 0x75, 0x41, 0xFA, + 0x00, 0x04, 0x4E, 0x75, 0x00, 0x1E, 0x00, 0x2A, 0x00, 0x2E, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, + 0x00, 0x3E, 0x00, 0x42, 0x00, 0x46, 0x00, 0x4A, 0x00, 0x4E, 0x00, 0x52, 0x00, 0x56, 0x00, 0x5A, + 0x00, 0x5E, 0x48, 0x6F, 0x73, 0x74, 0x45, 0x78, 0x67, 0x4C, 0x69, 0x62, 0x00, 0x00, 0x4E, 0xFA, + 0xFD, 0xCA, 0x4E, 0xFA, 0xFD, 0xD8, 0x4E, 0xFA, 0xFD, 0xF8, 0x4E, 0xFA, 0xFD, 0xE2, 0x4E, 0xFA, + 0xFE, 0x02, 0x4E, 0xFA, 0xFE, 0xCA, 0x4E, 0xFA, 0xFE, 0xDC, 0x4E, 0xFA, 0xFE, 0xEE, 0x4E, 0xFA, + 0xFF, 0x1A, 0x4E, 0xFA, 0xFF, 0x00, 0x4E, 0xFA, 0xFF, 0x28, 0x4E, 0xFA, 0xFF, 0x48, 0x4E, 0xFA, + 0xFF, 0x68, 0x4E, 0xFA, 0xFF, 0x84 +}; + +const int kHostExgLibSize = sizeof (kHostExgLib); + |