aboutsummaryrefslogtreecommitdiff
path: root/SrcShared/Miscellaneous.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcShared/Miscellaneous.cpp')
-rw-r--r--SrcShared/Miscellaneous.cpp2954
1 files changed, 2954 insertions, 0 deletions
diff --git a/SrcShared/Miscellaneous.cpp b/SrcShared/Miscellaneous.cpp
new file mode 100644
index 0000000..e8bded5
--- /dev/null
+++ b/SrcShared/Miscellaneous.cpp
@@ -0,0 +1,2954 @@
+/* -*- mode: C++; tab-width: 4 -*- */
+/* ===================================================================== *\
+ Copyright (c) 1999-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 "Miscellaneous.h"
+
+#include "Byteswapping.h" // Canonical
+#include "ChunkFile.h" // Chunk::GetPointer
+#include "EmBankMapped.h" // EmBankMapped::GetEmulatedAddress
+#include "EmErrCodes.h" // kError_UnimplementedTrap
+#include "EmHAL.h" // EmHAL::ResetTimer, EmHAL::ResetRTC
+#include "EmLowMem.h" // EmLowMem_SetGlobal, EmLowMem_GetGlobal
+#include "EmMemory.h" // Memory::MapPhysicalMemory, EmMem_strcpy, EmMem_memcmp
+#include "EmPalmFunction.h" // GetFunctionAddress
+#include "EmPatchState.h" // EmPatchState::OSMajorVersion
+#include "EmSession.h" // ScheduleDeferredError
+#include "EmStreamFile.h" // EmStreamFile, kOpenExistingForRead
+#include "ErrorHandling.h" // Errors::Throw
+#include "Logging.h" // LogDump
+#include "Platform.h" // Platform::AllocateMemory
+#include "ROMStubs.h" // WinGetDisplayExtent, FrmGetNumberOfObjects, FrmGetObjectType, FrmGetObjectId, ...
+#include "Strings.r.h" // kStr_INetLibTrapBase, etc.
+#include "UAE.h" // m68k_dreg, etc.
+
+#include <algorithm> // sort()
+#include <locale.h> // localeconv, lconv
+#include <strstream> // strstream
+#include <time.h> // time, localtime
+
+
+extern "C" {
+ // These are defined in machdep_maccess.h, too
+#undef get_byte
+#undef put_byte
+#undef put_long
+#include "gzip.h"
+#include "lzw.h"
+
+int (*write_buf_proc)(char *buf, unsigned size);
+
+DECLARE(uch, inbuf, INBUFSIZ +INBUF_EXTRA);
+DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA);
+DECLARE(ush, d_buf, DIST_BUFSIZE);
+DECLARE(uch, window, 2L*WSIZE);
+DECLARE(ush, tab_prefix, 1L<<BITS);
+
+int test = 0; /* test .gz file integrity */
+int level = 1; /* compression level */
+int exit_code = OK; /* program exit code */
+int verbose = 2; /* be verbose (-v) */
+int quiet = 0; /* be quiet (-q) */
+
+char ifname[] = "ifname";
+char ofname[] = "ofname";
+char* progname = "progname";
+
+long bytes_in; /* number of input bytes */
+long bytes_out; /* number of output bytes */
+int ifd; /* input file descriptor */
+int ofd; /* output file descriptor */
+unsigned insize; /* valid bytes in inbuf */
+unsigned inptr; /* index of next byte to be processed in inbuf */
+unsigned outcnt; /* bytes in output buffer */
+
+#include <setjmp.h>
+jmp_buf env;
+
+RETSIGTYPE abort_gzip()
+{
+ LogDump ();
+ abort ();
+// longjmp (env, 1);
+}
+
+int my_fprintf (FILE*, const char* fmt, ...)
+{
+ int n;
+ va_list arg;
+
+ va_start (arg, fmt);
+ n = LogGetStdLog ()->VPrintf (fmt, arg);
+ va_end (arg);
+
+ return n;
+}
+
+} // extern "C"
+
+
+extern "C"
+{
+ int PrvGzipReadProc (char* buf, unsigned size);
+ int PrvGzipWriteProc (char* buf, unsigned size);
+}
+
+static void* gSrcP;
+static void* gDstP;
+static long gSrcBytes;
+static long gDstBytes;
+static long gSrcOffset;
+static long gDstOffset;
+
+// ===========================================================================
+// ¥ StMemory Class
+// ===========================================================================
+// Constructor allocates the Ptr
+// Destructor disposes of the Ptr
+
+StMemory::StMemory(
+ char* inPtr)
+{
+ mIsOwner = (inPtr != NULL);
+ mPtr = inPtr;
+}
+
+StMemory::StMemory(
+ long inSize, // Bytes to allocate
+ Bool inClearBytes) // Whether to clear all bytes to zero
+{
+ mIsOwner = true;
+
+ if (inClearBytes)
+ mPtr = (char*) Platform::AllocateMemoryClear (inSize);
+ else
+ mPtr = (char*) Platform::AllocateMemory (inSize);
+}
+
+
+StMemory::StMemory(
+ const StMemory &inPointerBlock)
+{
+ UNUSED_PARAM(inPointerBlock)
+}
+
+
+StMemory::~StMemory()
+{
+ Dispose ();
+}
+
+
+StMemory&
+StMemory::operator = (
+ const StMemory &inPointerBlock)
+{
+ UNUSED_PARAM(inPointerBlock)
+
+ return *this;
+}
+
+
+void
+StMemory::Adopt(
+ char* inPtr)
+{
+ if (inPtr != mPtr)
+ {
+ Dispose ();
+
+ mIsOwner = (inPtr != NULL);
+ mPtr = inPtr;
+ }
+}
+
+
+char*
+StMemory::Release() const
+{
+ mIsOwner = false;
+ return mPtr;
+}
+
+
+void
+StMemory::Dispose()
+{
+ if (mIsOwner && (mPtr != NULL))
+ {
+ Platform::DisposeMemory (mPtr);
+ }
+
+ mIsOwner = false;
+ mPtr = NULL;
+}
+
+
+
+StMemoryMapper::StMemoryMapper (const void* memory, long size) :
+ fMemory (memory)
+{
+ if (fMemory)
+ Memory::MapPhysicalMemory (fMemory, size);
+}
+
+StMemoryMapper::~StMemoryMapper (void)
+{
+ if (fMemory)
+ Memory::UnmapPhysicalMemory (fMemory);
+}
+
+
+void* Platform_AllocateMemory (size_t size)
+{
+ return Platform::AllocateMemory (size);
+}
+
+
+void* Platform_ReallocMemory (void* p, size_t size)
+{
+ return Platform::ReallocMemory (p, size);
+}
+
+
+void Platform_DisposeMemory (void* p)
+{
+ Platform::DisposeMemory (p);
+}
+
+
+StWordSwapper::StWordSwapper (void* memory, long length) :
+ fMemory (memory),
+ fLength (length)
+{
+ ::ByteswapWords (fMemory, fLength);
+}
+
+StWordSwapper::~StWordSwapper (void)
+{
+ ::ByteswapWords (fMemory, fLength);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: PrvFormObjectHasValidSize
+ *
+ * DESCRIPTION: Determine whether or not a form object's size is valid.
+ *
+ * PARAMETERS: frm - form containing the object
+ *
+ * objType - the object's type
+ *
+ * objIndex - the object's index in the form
+ *
+ * bounds - the bounding rectangle around the object
+ *
+ * RETURNED: True if the object's size is valid.
+ *
+ ***********************************************************************/
+
+static Bool PrvFormObjectHasValidSize (FormPtr frm, FormObjectKind objType, UInt16 objIndex,
+ RectangleType bounds)
+{
+ EmAssert (frm);
+
+ CEnableFullAccess munge; // Remove blocks on memory access.
+
+ // Return valid right away if we have height and width
+
+ if (bounds.extent.x > 0 &&
+ bounds.extent.y > 0)
+ {
+ return true;
+ }
+
+ // Allow zero-sized gadgets and tables. The former are often
+ // used as dummy objects merely to hold references to custom
+ // data. The latter exist because there's no other way to
+ // hide a table (there's no "usable" bit).
+
+ if (objType == frmGadgetObj ||
+ objType == frmTableObj)
+ {
+ return true;
+ }
+
+ // Allow zero-width (but not zero-height) popup triggers.
+
+ if (objType == frmControlObj)
+ {
+ emuptr ctrlPtr = (emuptr) ::FrmGetObjectPtr (frm, objIndex);
+ uint8 style = EmMemGet8 (ctrlPtr + offsetof (ControlType, style));
+
+ if (style == popupTriggerCtl)
+ {
+ if (bounds.extent.x == 0 &&
+ bounds.extent.y > 0)
+ {
+ return true;
+ }
+ }
+ }
+
+ // Allow zero-height lists if the number of objects in them is zero.
+
+ if (objType == frmListObj)
+ {
+ emuptr listPtr = (emuptr) FrmGetObjectPtr (frm, objIndex);
+ Int16 numItems = ::LstGetNumberOfItems ((ListType*)listPtr);
+
+ if (numItems == 0)
+ {
+ if (bounds.extent.x > 0 &&
+ bounds.extent.y == 0)
+ {
+ return true;
+ }
+ }
+ }
+
+ // Failed all the special cases.
+
+ return false;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: PrvFormObjectIsOffscreen
+ *
+ * DESCRIPTION: Determine whether or not an object is off-screen.
+ *
+ * PARAMETERS: bounds - the bounding rectangle of the object
+ *
+ * winWidth - width of the window
+ *
+ * winHeight - width of the window
+ *
+ * RETURNED: True if the object is off-screen.
+ *
+ ***********************************************************************/
+
+static Bool PrvFormObjectIsOffscreen (RectangleType bounds, Int16 winWidth, Int16 winHeight)
+{
+ // Ignore objects with a zero extent
+
+ if (bounds.extent.x <= 0 ||
+ bounds.extent.y <= 0)
+ return false;
+
+ return (bounds.topLeft.x >= winWidth ||
+ bounds.topLeft.y >= winHeight ||
+ bounds.topLeft.x + bounds.extent.x <= 0 ||
+ bounds.topLeft.y + bounds.extent.y <= 0);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: PrvFormObjectIsUsable
+ *
+ * DESCRIPTION: Determines whether or not a form object is usable, ie
+ * should be considered part of the UI.
+ *
+ * PARAMETERS: frmP - form containing the object in question
+ *
+ * index - index of the object
+ *
+ * kind - type of the object
+ *
+ * RETURNED: True if the object is usable.
+ *
+ ***********************************************************************/
+
+
+#define ControlAttrType_usable 0x8000 // set if part of ui
+
+
+static Bool PrvFormObjectIsUsable (FormPtr frmP, uint16 index, FormObjectKind kind)
+{
+ EmAssert (frmP);
+
+ CEnableFullAccess munge; // Remove blocks on memory access.
+
+ emuptr objP = (emuptr)::FrmGetObjectPtr (frmP, index);
+
+ if (objP == EmMemNULL)
+ {
+ return false;
+ }
+
+ switch (kind)
+ {
+ // Objects with special 'usable' flag:
+
+ case frmFieldObj:
+ {
+ FieldAttrType attr;
+ ::FldGetAttributes ((FieldType*)objP, &attr);
+
+ return attr.usable == true;
+ }
+
+ case frmControlObj:
+ {
+ EmAliasControlType<PAS> control ((emuptr)objP);
+
+ return control.attr.flags & ControlAttrType_usable;
+ }
+
+ case frmGadgetObj:
+ {
+ EmAliasFormGadgetType<PAS> gadget ((emuptr)objP);
+
+ return gadget.attr.flags & ControlAttrType_usable;
+ }
+
+ case frmListObj:
+ {
+ EmAliasListType<PAS> list ((emuptr)objP);
+
+ return list.attr.flags & ControlAttrType_usable;
+ }
+
+ case frmScrollBarObj:
+ {
+ EmAliasScrollBarType<PAS> scrollbar ((emuptr)objP);
+
+ return scrollbar.attr.flags & ControlAttrType_usable;
+ }
+
+ // Objects assumed to be usable:
+
+ case frmTableObj:
+ case frmGraffitiStateObj:
+
+ return true;
+
+ // Objects assumed to be unusable:
+
+ case frmBitmapObj:
+ case frmLineObj:
+ case frmFrameObj:
+ case frmRectangleObj:
+ case frmLabelObj:
+ case frmPopupObj:
+ case frmTitleObj:
+
+ return false;
+ }
+
+ // Everything else:
+
+ return false;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: ValidateFormObjects
+ *
+ * DESCRIPTION: Iterate over all the objects in a form and complain
+ * if we find one that is invalid in some way.
+ *
+ * PARAMETERS: frm - the form to validate
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void ValidateFormObjects (FormPtr frm)
+{
+ if (!frm)
+ return;
+
+ Int16 winWidth, winHeight;
+ ::WinGetDisplayExtent (&winWidth, &winHeight);
+
+ UInt16 numObjects = ::FrmGetNumberOfObjects (frm);
+ for (UInt16 objIndex = 0; objIndex < numObjects; ++objIndex)
+ {
+ FormObjectKind objType = ::FrmGetObjectType (frm, objIndex);
+ UInt16 objID = ::FrmGetObjectId (frm, objIndex);
+
+ switch (objType)
+ {
+ case frmBitmapObj:
+ case frmLineObj:
+ case frmFrameObj:
+ case frmRectangleObj:
+ case frmLabelObj:
+ case frmTitleObj:
+ case frmPopupObj:
+ // do nothing for these
+ break;
+
+ default:
+ {
+ // Check for completely offscreen objects.
+ // (The jury is still out on partially offscreen objects.)
+ RectangleType bounds;
+ ::FrmGetObjectBounds (frm, objIndex, &bounds);
+
+ if (!::PrvFormObjectHasValidSize (frm, objType, objIndex, bounds))
+ {
+ // Report any errors. For now, don't report errors on 1.0
+ // devices. They may not follow the rules, either. In
+ // particular, someone noticed that the Graffiti state
+ // indicator has a size of 0,0.
+
+ if (EmPatchState::OSMajorVersion () > 1)
+ {
+ EmAssert (gSession);
+ gSession->ScheduleDeferredError (
+ new EmDeferredErrSizelessObject (objID, bounds));
+ }
+ }
+ else if (::PrvFormObjectIsOffscreen (bounds, winWidth, winHeight))
+ {
+ if (EmPatchState::OSMajorVersion () > 1)
+ {
+ EmAssert (gSession);
+ gSession->ScheduleDeferredError (
+ new EmDeferredErrOffscreenObject (objID, bounds));
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: CollectOKObjects
+ *
+ * DESCRIPTION: Iterate over the objects in a form and make a list of
+ * the ones that are fair game for tapping on. Exclude
+ * objects that aren't interactive (ie a label), aren't
+ * usable, or aren't valid.
+ *
+ * PARAMETERS: frm - the form in question
+ *
+ * okObjects - vector list of objects that are deemed 'ok'.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void CollectOKObjects (FormPtr frm, vector<UInt16>& okObjects)
+{
+ if (!frm)
+ return;
+
+ Int16 winWidth, winHeight;
+ ::WinGetDisplayExtent (&winWidth, &winHeight);
+
+ UInt16 numObjects = ::FrmGetNumberOfObjects (frm);
+ for (UInt16 objIndex = 0; objIndex < numObjects; ++objIndex)
+ {
+ FormObjectKind objType = ::FrmGetObjectType (frm, objIndex);
+
+ switch (objType)
+ {
+ case frmBitmapObj:
+ case frmLineObj:
+ case frmFrameObj:
+ case frmRectangleObj:
+ case frmLabelObj:
+ case frmTitleObj:
+ case frmPopupObj:
+ // do nothing for these
+ break;
+
+ default:
+ {
+ RectangleType bounds;
+ ::FrmGetObjectBounds (frm, objIndex, &bounds);
+
+ if (!::PrvFormObjectHasValidSize (frm, objType, objIndex, bounds) ||
+ ::PrvFormObjectIsOffscreen (bounds, winWidth, winHeight) ||
+ !::PrvFormObjectIsUsable (frm, objIndex, objType))
+ {
+ break;
+ }
+ else
+ {
+ okObjects.push_back (objIndex);
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ PinRectInRect
+// ---------------------------------------------------------------------------
+
+Bool PinRectInRect (EmRect& inner, const EmRect& outer)
+{
+ // Do this in a way such that if the incoming rectangle is
+ // taller or wider than gdRect that we ensure we see the
+ // top and left.
+
+ Bool result = false;
+
+ if (inner.fBottom > outer.fBottom)
+ {
+ inner.Offset (0, outer.fBottom - inner.fBottom); // Move it up
+ result = true;
+ }
+
+ if (inner.fRight > outer.fRight)
+ {
+ inner.Offset (outer.fRight - inner.fRight, 0); // Move it left
+ result = true;
+ }
+
+ if (inner.fTop < outer.fTop)
+ {
+ inner.Offset (0, outer.fTop - inner.fTop); // Move it down
+ result = true;
+ }
+
+ if (inner.fLeft < outer.fLeft)
+ {
+ inner.Offset (outer.fLeft - inner.fLeft, 0); // Move it right
+ result = true;
+ }
+
+ return result;
+}
+
+
+// For "data" databases, we look to see if they have the following structure
+// in their appInfo block. If so, we grab the icon and version string from here.
+// WARNING: This structure contains variable size fields and must be generated
+// and parsed at programmatically.
+#define dataAppInfoSignature 'lnch'
+#define dataAppInfoVersion 3 // current version of header
+
+#include "PalmPack.h"
+
+struct DataAppInfoType
+{
+ UInt32 signature; // must be 'lnch' (0x6C6E6338)
+ UInt16 hdrVersion; // version of this header - must be 3
+ UInt16 encVersion; // encoder version
+ UInt16 verStrWords; // length of version string array that
+ // follows in 16-bit words.
+ //UInt8 verStr[verStrWords]; // 0 terminated version string with
+ // possible extra NULL byte at end for
+ // padding
+
+ //--- The title is only present in version 2 or later
+ UInt16 titleWords; // length of title string array that follows
+ // in 16-bit words.
+ //UInt8 title[titleWords]; // 0 terminated title string with possible
+ // extra NULL at end for padding.
+
+
+ UInt16 iconWords; // length of icon data that follows in 16-bit
+ // words.
+ //UInt8 icon[iconWords]; // icon in "BitmapType" format with possible NULL
+ // byte at end for even UInt16 padding
+ UInt16 smIconWords; // length of small icon data that follows in
+ // 16-bit words
+ //UInt8 smIcon[smIconWords]; // small icon in "BitmapType" format with
+ // possible NULL byte at end for even UInt16
+ // padding
+ //--------- Version 2 Fields ------------
+};
+
+// Size of appInfo block.
+#define dataAppInfoVersionSize (sizeof(DataAppInfoType))
+
+#include "PalmPackPop.h"
+
+
+/***********************************************************************
+ *
+ * FUNCTION: IsExecutable
+ *
+ * DESCRIPTION: Returns whether or not the given database contains an
+ * application in which case we want to present only
+ * the database with the most recent version number (in
+ * case there's more than one database with this one's
+ * type and creator).
+ *
+ * PARAMETERS: dbType - type of the database
+ *
+ * dbCreator - creator signature of the database
+ *
+ * dbAttrs - attributes of the database
+ *
+ * RETURNED: True if this database is an executable.
+ *
+ ***********************************************************************/
+
+Bool IsExecutable (UInt32 dbType, UInt32 dbCreator, UInt16 dbAttrs)
+{
+ UNUSED_PARAM(dbCreator)
+ UNUSED_PARAM(dbAttrs)
+
+ if (dbType == sysFileTApplication)
+ return true;
+
+ if (dbType == sysFileTPanel)
+ return true;
+
+ return false;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: IsVisible
+ *
+ * DESCRIPTION: Returns whether or not the given database represents
+ * an item we want to display.
+ *
+ * PARAMETERS: dbType - type of the database
+ *
+ * dbCreator - creator signature of the database
+ *
+ * dbAttrs - attributes of the database
+ *
+ * RETURNED: True if we should include this database in our list.
+ *
+ ***********************************************************************/
+
+Bool IsVisible (UInt32 dbType, UInt32 dbCreator, UInt16 dbAttrs)
+{
+ UNUSED_PARAM(dbCreator)
+
+ // Don't show anything concerning the Launcher
+ // (That comment and the following commented out code was from the
+ // Launcher application. I've take it out so that we can run
+ // Gremlins over the Launcher).
+// if (dbCreator == sysFileCLauncher)
+// return false;
+
+ // The following test can come and go. Currently, it's here
+ // so that things like Clipper don't show up in the list (just
+ // as it doesn't show up in the Launcher). However, there may
+ // be time when we want to show it. An example would be in
+ // an experiemental version of the New Gremlin dialog that
+ // separated apps and documents. Selecting an app in one list
+ // would display a list of its documents in the other list. In
+ // that case, we'd need to show clipper in order to be able to
+ // display its documents.
+
+ // OK, the test is now gone. From Scott Johnson:
+ //
+ // The New Gremlin list doesn't show apps with the dmHdrAttrHidden attribute.
+ // This is a problem for mine, which is a sort of runtime forms engine. The
+ // runtime is a hidden app and the user-built apps are visible. The user app
+ // launches by just doing an app switch to the runtime. (Sort of a launchable
+ // database concept for pre-3.2 systems.) To Gremlin this, both apps need to
+ // be selected in the New Gremlin list. But the hidden one isn't shown. Oops.
+
+
+// if (dbAttrs & dmHdrAttrHidden)
+// return false;
+
+ if (dbAttrs & dmHdrAttrLaunchableData)
+ return true;
+
+ if (dbType == sysFileTApplication)
+ return true;
+
+ if (dbType == sysFileTPanel)
+ return true;
+
+ return false;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GetLoadableFileList
+ *
+ * DESCRIPTION: Scan the given directory for files that can be loaded
+ * into the Palm OS environment and add them to the
+ * given collection object. Loadable files are PRCs,
+ * PDBs, and PQAs.
+ *
+ * PARAMETERS: directoryName - name of the directory to search.
+ * This directory is assumed to be in the emulator's
+ * containing directory.
+ *
+ * fileList - collection to receive the found files.
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+void GetLoadableFileList (string directoryName, EmFileRefList& fileList)
+{
+ // Get the application's directory.
+
+ EmDirRef poserDir = EmDirRef::GetEmulatorDirectory ();
+
+ // Get the directory we're asked to look into.
+
+ EmDirRef searchDir (poserDir, directoryName);
+ if (!searchDir.Exists ())
+ return;
+
+ // Get all of its children.
+
+ EmFileRefList children;
+ searchDir.GetChildren (&children, NULL);
+
+ // Filter for the types that we want.
+
+ EmFileRefList::iterator iter = children.begin ();
+ while (iter != children.end ())
+ {
+ if (iter->IsType (kFileTypePalmApp) ||
+ iter->IsType (kFileTypePalmDB) ||
+ iter->IsType (kFileTypePalmQA))
+ {
+ fileList.push_back (*iter);
+ }
+
+ ++iter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GetFileContents
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS:
+ *
+ * RETURNED:
+ *
+ ***********************************************************************/
+
+void GetFileContents (const EmFileRef& file, Chunk& contents)
+{
+ EmStreamFile stream (file, kOpenExistingForRead);
+ int32 length = stream.GetLength ();
+
+ contents.SetLength (length);
+
+ stream.GetBytes (contents.GetPointer (), length);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: AppGetExtraInfo
+ *
+ * DESCRIPTION: Returns additional information on the application. This
+ * information is usually pretty expensive to get, so we
+ * defer getting it until as late as possible.
+ *
+ * This function is derived from one in the Launcher with
+ * the same name. That function returned a lot more
+ * information. This one has been simplified to return
+ * only the application's (or special database's) name.
+ *
+ * PARAMETERS: infoP - pointer to the DatabaseInfo struct for the application
+ * we need to get more information on.
+ *
+ * RETURNED: Any errors encountered while processing this request.
+ * The requested information is returned back in the DatabaseInfo
+ * struct.
+ *
+ ***********************************************************************/
+
+static Err AppGetExtraInfo (DatabaseInfo* infoP)
+{
+ Err err = errNone;
+
+ infoP->name[0] = 0;
+
+ //====================================================================
+ // If it's a resource database, we must open it to get the appName
+ //====================================================================
+ if (infoP->dbAttrs & dmHdrAttrResDB)
+ {
+ DmOpenRef appDB = NULL;
+ MemHandle strH;
+
+ // Open database
+ appDB = DmOpenDatabase (infoP->cardNo, infoP->dbID, dmModeReadOnly);
+ if (appDB == NULL)
+ {
+ err = DmGetLastErr ();
+ goto Exit;
+ }
+
+ //...............................
+ // Get app name if we don't already have it.
+ //...............................
+ strH = DmGet1Resource (ainRsc, ainID);
+
+ // copy launcher name, if present
+ if (strH != NULL)
+ {
+ emuptr strP = (emuptr) MemHandleLock (strH);
+ EmMem_strcpy (infoP->name, strP);
+ MemHandleUnlock (strH);
+ DmReleaseResource (strH);
+ }
+
+ ::DmCloseDatabase (appDB);
+ } // if resource database
+
+ //====================================================================
+ // If it's a record database, we look in the appInfo block.
+ //====================================================================
+ else
+ {
+ LocalID appInfoID;
+ MemHandle appInfoH = 0;
+ MemPtr appInfoP = 0;
+ emuptr specialInfoP;
+ emuptr bP;
+ UInt16 verStrWords, titleWords;
+
+ // Look for app info
+ err = DmDatabaseInfo (infoP->cardNo, infoP->dbID, 0,
+ 0, 0, 0, 0, 0, 0, &appInfoID, 0, 0, 0);
+
+ if (!err && appInfoID)
+ {
+ // Get handle (if RAM based) and ptr to app Info
+ if (MemLocalIDKind (appInfoID) == memIDHandle)
+ {
+ appInfoH = (MemHandle) MemLocalIDToGlobal (appInfoID, infoP->cardNo);
+ appInfoP = MemHandleLock (appInfoH);
+ }
+ else
+ {
+ appInfoP = MemLocalIDToGlobal(appInfoID, infoP->cardNo);
+ }
+
+ // See if this is the special launcher info and if so, get the icons
+ // out of that.
+ specialInfoP = (emuptr) appInfoP;
+ DataAppInfoType specialInfo;
+
+ specialInfo.signature = EmMemGet32 (specialInfoP + offsetof (DataAppInfoType, signature));
+ specialInfo.hdrVersion = EmMemGet16 (specialInfoP + offsetof (DataAppInfoType, hdrVersion));
+ specialInfo.encVersion = EmMemGet16 (specialInfoP + offsetof (DataAppInfoType, encVersion));
+
+ if (MemPtrSize (appInfoP) >= dataAppInfoVersionSize &&
+ specialInfo.signature == dataAppInfoSignature &&
+ specialInfo.hdrVersion >= dataAppInfoVersion)
+ {
+ // Get ptr to version string
+ bP = specialInfoP + offsetof (DataAppInfoType, verStrWords);
+ verStrWords = EmMemGet16 (bP);
+ bP += sizeof(UInt16);
+ bP += verStrWords * sizeof(UInt16);
+
+ // Get ptr to name string
+ titleWords = EmMemGet16 (bP);
+ bP += sizeof(UInt16);
+ if (titleWords)
+ {
+ EmMem_strcpy (infoP->name, bP);
+ }
+ } // If valid appInfo
+
+ if (appInfoH)
+ {
+ MemHandleUnlock(appInfoH);
+ }
+ } // if (!err && appInfoID)
+ } // Record Database.
+
+Exit:
+
+ // If no luck getting the visible name, put in default
+ if (infoP->name[0] == 0)
+ {
+ // Get DB name
+ strcpy (infoP->name, infoP->dbName);
+ }
+
+ return err;
+}
+
+
+
+/***********************************************************************
+ *
+ * FUNCTION: AppCompareDataBaseNames
+ *
+ * DESCRIPTION: sort() callback function to sort entries by name.
+ *
+ * PARAMETERS: a, b - references to two DatabaseInfo's to compare.
+ *
+ * RETURNED: True if a should appear before b, false otherwise.
+ *
+ ***********************************************************************/
+
+static bool AppCompareDataBaseNames (const DatabaseInfo& a, const DatabaseInfo& b)
+{
+ return _stricmp (a.name, b.name) < 0;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GetDatabases
+ *
+ * DESCRIPTION: Collects the list of entries that should be displayed
+ * in the New Gremlin dialog box.
+ *
+ * This function is derived from the Launcher function
+ * AppCreateDataBaseList, as rewritten by Ron for the
+ * 3.2 ROMs.
+ *
+ * PARAMETERS: dbList -- collection into which we store the found
+ * DatabaseInfo entries.
+ *
+ * RETURNED: nothing.
+ *
+ ***********************************************************************/
+
+void GetDatabases (DatabaseInfoList& dbList, Bool applicationsOnly)
+{
+ UInt16 cardNo;
+ UInt16 numCards;
+ UInt16 numDBs;
+ Int16 dbIndex; // UInt16 results in a bug
+ LocalID dbID;
+ Err err = errNone;
+ DatabaseInfo dbInfo;
+ Boolean needToAddNewEntry;
+
+ //=======================================================================
+ // Cycle through all databases in the ROM and RAM and place them into our list.
+ //=======================================================================
+ numCards = ::MemNumCards ();
+ for (cardNo = 0; cardNo < numCards; ++cardNo)
+ {
+ numDBs = ::DmNumDatabases (cardNo);
+
+ //---------------------------------------------------------------
+ // Loop through databases on this card, DmGetDatabase() returns ROM
+ // databases first, followed by RAM databases.
+ //---------------------------------------------------------------
+ for (dbIndex = 0; dbIndex < numDBs; ++dbIndex)
+ {
+ //--------------------------------------------------------
+ // Get info on the next database and see if it should be visible.
+ //--------------------------------------------------------
+ dbID = ::DmGetDatabase (cardNo, dbIndex);
+ err = ::DmDatabaseInfo (
+ cardNo,
+ dbID,
+ dbInfo.dbName, /*nameP*/
+ &dbInfo.dbAttrs,
+ &dbInfo.version,
+ NULL, /*create date*/
+ &dbInfo.modDate,
+ NULL, /*backup date*/
+ NULL, /*modNum*/
+ NULL, /*appInfoID*/
+ NULL, /*sortInfoID*/
+ &dbInfo.type,
+ &dbInfo.creator);
+
+ Errors::ThrowIfPalmError (err);
+
+
+ // If it's not supposed to be visible, skip it
+ if (applicationsOnly && !::IsVisible (dbInfo.type, dbInfo.creator, dbInfo.dbAttrs))
+ {
+ continue;
+ }
+
+ //--------------------------------------------------------------
+ // Save info on this database
+ //--------------------------------------------------------------
+ dbInfo.dbID = dbID;
+ dbInfo.cardNo = cardNo;
+
+ //--------------------------------------------------------------
+ // If it's an executable, make sure it's the most recent version in our
+ // list
+ //--------------------------------------------------------------
+ needToAddNewEntry = true;
+ if (applicationsOnly && ::IsExecutable (dbInfo.type, dbInfo.creator, dbInfo.dbAttrs))
+ {
+ // Search for database of same type and creator and check version
+ DatabaseInfoList::iterator thisIter = dbList.begin ();
+ while (thisIter != dbList.end ())
+ {
+ if ((*thisIter).type == dbInfo.type &&
+ (*thisIter).creator == dbInfo.creator)
+ {
+ // If this new one is a newer or same version than the previous one,
+ // replace the previous entry. Checking for == version allows RAM
+ // executables to override ROM ones.
+ if (dbInfo.version >= (*thisIter).version)
+ {
+ ::AppGetExtraInfo (&dbInfo);
+ *thisIter = dbInfo;
+ }
+
+ // Since there's already an item with this type/creator
+ // already in the list, there's no need to add another one.
+ needToAddNewEntry = false;
+
+ break;
+ }
+
+ ++thisIter;
+ }
+ }
+
+
+ //--------------------------------------------------------------
+ // If we still need to add this entry, do so now.
+ //--------------------------------------------------------------
+ if (needToAddNewEntry)
+ {
+ ::AppGetExtraInfo (&dbInfo);
+ dbList.push_back (dbInfo);
+ }
+ } // for (dbIndex = 0; dbIndex < numDBs; dbIndex++)
+ } // for (cardNo = 0; cardNo < MemNumCards(); cardNo++)
+
+
+ //===========================================================================
+ // Sort the list by name
+ //===========================================================================
+ // Sort the databases by their name.
+ sort (dbList.begin (), dbList.end (), AppCompareDataBaseNames);
+}
+
+
+
+/***********************************************************************
+ *
+ * FUNCTION: InstallCalibrationInfo
+ *
+ * DESCRIPTION: Sets the pen calibration info to be "perfect": no
+ * translation or scaling.
+ *
+ * PARAMETERS: none
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+void InstallCalibrationInfo (void)
+{
+ // Open the preferences database. If the new version of PrefOpenPreferenceDB
+ // exists, then call it. Otherwise, call the old version. We can't just
+ // unconditionally call the old version, as that has a bug in the newer
+ // ROMs that causes it to create the database incorrectly if it doesn't
+ // already exist.
+
+ DmOpenRef dbP;
+ if (EmLowMem::TrapExists (sysTrapPrefOpenPreferenceDB))
+ dbP = ::PrefOpenPreferenceDB (false);
+ else
+ dbP = ::PrefOpenPreferenceDBV10 ();
+
+ if (dbP)
+ {
+ // Get the calibration information.
+
+ MemHandle resourceH = ::DmGetResource (sysResTSysPref, sysResIDSysPrefCalibration);
+
+ // If that information doesn't exist, go about creating it.
+
+ if (!resourceH)
+ {
+ resourceH = ::DmNewResource (dbP, sysResTSysPref, sysResIDSysPrefCalibration,
+ 4 * sizeof(UInt16));
+ }
+
+ if (resourceH)
+ {
+ // Write in the calibration information. The information has the
+ // following format and values:
+ //
+ // scaleX : 256
+ // scaleY : 256
+ // offsetX : 0
+ // offsetY : 0
+ //
+ // We encode that data here as a string of bytes to avoid endian problems.
+
+ MemPtr resP = ::MemHandleLock (resourceH);
+
+ unsigned char data[] = { 1, 0, 1, 0, 0, 0, 0, 0 };
+
+ ::DmWrite (resP, 0, data, 4 * sizeof (UInt16));
+
+ ::MemHandleUnlock (resourceH);
+ ::DmReleaseResource (resourceH);
+ }
+
+ ::DmCloseDatabase (dbP);
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: ResetCalibrationInfo
+ *
+ * DESCRIPTION: Sets the pen calibration info to be "perfect": no
+ * translation or scaling.
+ *
+ * PARAMETERS: none
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+void ResetCalibrationInfo (void)
+{
+ // Reset the pen calibration info by calling PenCalibrate with the right
+ // parameters. Unfortunately, due presumably to division rounding errors,
+ // doing this just once doesn't necessarily get the scaling and offset
+ // values exactly. However, making a few calls to PenCalibrate seems
+ // to get us to home in on the perfect calibration values.
+
+ Bool perfect = false;
+
+ for (int kk = 0; kk < 3 && !perfect; ++kk)
+ {
+ #define target0X 10 // top left
+ #define target0Y 10
+ #define target1X (160-10) // bottom right
+ #define target1Y (160-10)
+
+ Err err;
+ PointType digPoints[2];
+ PointType scrPoints[2];
+
+ scrPoints[0].x = target0X;
+ scrPoints[0].y = target0Y;
+ scrPoints[1].x = target1X;
+ scrPoints[1].y = target1Y;
+
+ digPoints[0].x = 0x100 - target0X;
+ digPoints[0].y = 0x100 - target0Y;
+ digPoints[1].x = 0x100 - target1X;
+ digPoints[1].y = 0x100 - target1Y;
+
+ err = ::PenRawToScreen(&digPoints[0]);
+ err = ::PenRawToScreen(&digPoints[1]);
+ err = ::PenCalibrate(&digPoints[0], &digPoints[1], &scrPoints[0], &scrPoints[1]);
+
+ DmOpenRef dbP;
+ if (EmLowMem::TrapExists (sysTrapPrefOpenPreferenceDB))
+ dbP = ::PrefOpenPreferenceDB (false);
+ else
+ dbP = ::PrefOpenPreferenceDBV10 ();
+
+ if (dbP)
+ {
+ MemHandle resourceH = ::DmGetResource (sysResTSysPref, sysResIDSysPrefCalibration);
+
+ if (resourceH)
+ {
+ MemPtr resP = ::MemHandleLock (resourceH);
+ unsigned char perfect_pattern[] = { 1, 0, 1, 0, 0, 0, 0, 0 };
+
+ perfect = (EmMem_memcmp ((void*) perfect_pattern, (emuptr) resP, 8) == 0);
+
+ ::MemHandleUnlock (resourceH);
+ ::DmReleaseResource (resourceH);
+ }
+
+ ::DmCloseDatabase (dbP);
+ }
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: ResetClocks
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+void ResetClocks (void)
+{
+ EmHAL::ResetTimer ();
+
+ EmLowMem_SetGlobal (hwrCurTicks, 0);
+
+ emuptr sysKernelDataP = EmLowMem_GetGlobal (sysKernelDataP);
+ EmMemPut32 (sysKernelDataP + 0x20, 0);
+
+ EmHAL::ResetRTC ();
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: SetHotSyncUserName
+ *
+ * DESCRIPTION: Calls the Data Link Manager to set the user's HotSync
+ * name. Many applications key off this name for things
+ * like copy protection, so we set this value when they
+ * boot up.
+ *
+ * PARAMETERS: userNameP - the user's name.
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+const UInt16 kMagicRefNum = 0x666; // See comments in HtalLibSendReply.
+
+void SetHotSyncUserName (const char* userNameP)
+{
+ if (EmLowMem::GetTrapAddress (sysTrapDlkDispatchRequest) == EmMemNULL)
+ return;
+
+ if (!userNameP)
+ return;
+
+ size_t userNameLen = strlen (userNameP) + 1;
+
+ // If the name is too long, just return. This should really only
+ // happen if the user hand-edits the preference file to contain
+ // a name that's too long. The Preferences dialog box handler
+ // checks as well, so the name shouldn't get too long from that path.
+
+ if (userNameLen > dlkMaxUserNameLength + 1)
+ return;
+
+ // We need to prepare a command block for the DataLink Manager.
+ // Define one large enough to hold all the data we'll pass in.
+ //
+ // The format of the data block is as follows:
+ //
+ // [byte] DlpReqHeaderType.id : Command request number (== dlpWriteUserInfo)
+ // [byte] DlpReqHeaderType.argc : # of arguments for this command (== 1)
+ //
+ // [byte] DlpTinyArgWrapperType.bID : ID of first argument (== dlpWriteUserInfoReqArgID)
+ // [byte] DlpTinyArgWrapperType.bSize : Size in bytes of first argument (== whatever)
+ //
+ // [long] DlpWriteUserInfoReqHdrType.userID : Not used here - set to zero
+ // [long] DlpWriteUserInfoReqHdrType.viewerID : Not used here - set to zero
+ // [long] DlpWriteUserInfoReqHdrType.lastSyncPC : Not used here - set to zero
+ // [8byt] DlpWriteUserInfoReqHdrType.lastSyncDate : Not used here - set to zero
+ // [long] DlpWriteUserInfoReqHdrType.modFlags : Bits saying what values are being set
+ // [byte] DlpWriteUserInfoReqHdrType.userNameLen : Length of user name + NULL
+ //
+ // [str ] userName
+
+ char buffer[ sizeof (DlpReqHeaderType) +
+ sizeof (DlpTinyArgWrapperType) +
+ sizeof (DlpWriteUserInfoReqHdrType) +
+ dlpMaxUserNameSize];
+
+ // Get handy pointers to all of the above.
+ DlpReqHeaderType* reqHdr = (DlpReqHeaderType*) buffer;
+ DlpTinyArgWrapperType* reqWrapper = (DlpTinyArgWrapperType*) (((char*) reqHdr) + sizeof(DlpReqHeaderType));
+ DlpWriteUserInfoReqHdrType* reqArgHdr = (DlpWriteUserInfoReqHdrType*) (((char*) reqWrapper) + sizeof(DlpTinyArgWrapperType));
+ char* reqName = ((char*) reqArgHdr) + sizeof (DlpWriteUserInfoReqHdrType);
+
+ // Fill in request header
+ reqHdr->id = dlpWriteUserInfo;
+ reqHdr->argc = 1;
+
+ // Fill in the request arg wrapper
+ reqWrapper->bID = (UInt8) dlpWriteUserInfoReqArgID;
+ reqWrapper->bSize = (UInt8) (sizeof (*reqArgHdr) + userNameLen);
+
+ // Fill in request arg header
+ reqArgHdr->modFlags = dlpUserInfoModName;
+ reqArgHdr->userNameLen = userNameLen;
+
+ // Copy in the user name.
+ strcpy (reqName, userNameP);
+
+ // Build up a session block to hold the command block.
+ DlkServerSessionType session;
+ memset (&session, 0, sizeof (session));
+ session.htalLibRefNum = kMagicRefNum; // See comments in HtalLibSendReply.
+ session.gotCommand = true;
+ session.cmdLen = sizeof (buffer);
+ session.cmdP = buffer;
+
+ // For simplicity, byteswap here so that we don't have to reparse all
+ // that above data in DlkDispatchRequest.
+
+ Canonical (reqHdr->id);
+ Canonical (reqHdr->argc);
+
+ Canonical (reqWrapper->bID);
+ Canonical (reqWrapper->bSize);
+
+ Canonical (reqArgHdr->modFlags);
+ Canonical (reqArgHdr->userNameLen);
+
+ // Patch up cmdP and map in the buffer it points to.
+
+ StMemoryMapper mapper (session.cmdP, session.cmdLen);
+ session.cmdP = (void*) EmBankMapped::GetEmulatedAddress (session.cmdP);
+
+ // Finally, install the name.
+ /*Err err =*/ DlkDispatchRequest (&session);
+
+#if 0
+ ULong lastSyncDate;
+ char userName[dlkUserNameBufSize];
+ Err err = DlkGetSyncInfo(0/*succSyncDateP*/, &lastSyncDate, 0/*syncStateP*/,
+ userName, 0/*logBufP*/, 0/*logLenP*/);
+#endif
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: SeparateList
+ *
+ * DESCRIPTION: Break up a comma-delimited list of items, returning the
+ * pieces in a StringList.
+ *
+ * PARAMETERS: stringList - the StringList to receive the broken-up
+ * pieces of the comma-delimited list. This collection
+ * is *not* first cleared out, so it's possible to add
+ * to the collection with this function.
+ *
+ * str - the string containing the comma-delimited items.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void SeparateList (StringList& stringList, string str, char delimiter)
+{
+ string::size_type offset;
+
+ while ((offset = str.find (delimiter)) != string::npos)
+ {
+ string nextElement = str.substr (0, offset);
+ str = str.substr (offset + 1);
+ stringList.push_back (nextElement);
+ }
+
+ stringList.push_back (str);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: RunLengthEncode
+ *
+ * DESCRIPTION: Pack data according to the scheme used in QuickDraw's
+ * PackBits routine. This is the format, according to
+ * Macintosh Technote 1023:
+ *
+ * The first byte is a flag-counter byte that specifies
+ * whether or not the following data is packed, and the
+ * number of bytes involved.
+ *
+ * If this first byte is a negative number, the following
+ * data is packed and the number is a zero-based count of
+ * the number of times the data byte repeats when expanded.
+ * There is one data byte following the flag-counter byte
+ * in packed data; the byte after the data byte is the next
+ * flag-counter byte.
+ *
+ * If the flag-counter byte is a positive number, then the
+ * following data is unpacked and the number is a zero-based
+ * count of the number of incompressible data bytes that
+ * follow. There are (flag-counter+1) data bytes following
+ * the flag-counter byte. The byte after the last data byte
+ * is the next flag-counter byte.
+ *
+ * Consider the following example:
+ *
+ * Unpacked data:
+ *
+ * AA AA AA 80 00 2A AA AA AA AA 80 00
+ * 2A 22 AA AA AA AA AA AA AA AA AA AA
+ *
+ * After being packed by PackBits:
+ *
+ * FE AA ; (-(-2)+1) = 3 bytes of the pattern $AA
+ * 02 80 00 2A ; (2)+1 = 3 bytes of discrete data
+ * FD AA ; (-(-3)+1) = 4 bytes of the pattern $AA
+ * 03 80 00 2A 22 ; (3)+1 = 4 bytes of discrete data
+ * F7 AA ; (-(-9)+1) = 10 bytes of the pattern $AA
+ *
+ * or
+ *
+ * FE AA 02 80 00 2A FD AA 03 80 00 2A 22 F7 AA
+ * * * * * *
+ *
+ * The bytes with the asterisk (*) under them are the
+ * flag-counter bytes. PackBits packs the data only when
+ * there are three or more consecutive bytes with the same
+ * data; otherwise it just copies the data byte for byte
+ * (and adds the count byte).
+ *
+ * PARAMETERS: srcPP - pointer to the pointer to the source bytes. The
+ * referenced pointer gets udpated to point past the
+ * last byte included the packed output.
+ *
+ * dstPP - pointer to the pointer to the destination buffer.
+ * The referenced pointer gets updated to point past
+ * the last byte stored in the output buffer.
+ *
+ * srcBytes - length of the buffer referenced by srcPP
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void RunLengthEncode (void** srcPP, void** dstPP, long srcBytes, long dstBytes)
+{
+ UNUSED_PARAM(dstBytes)
+
+ enum { kBeginRun, kRepeatRun, kCopyRun };
+
+ uint8* srcP = (uint8*) *srcPP;
+ uint8* dstP = (uint8*) *dstPP;
+ uint8* opP = NULL;
+ long sample[3] = { -1, -1, -1 }; // Type must be > uint8 so that it can hold -1.
+ long opCount = 0;
+ long state = kBeginRun;
+
+ for (srcBytes += 1; srcBytes >= 0; --srcBytes)
+ {
+ sample[0] = sample[1];
+ sample[1] = sample[2];
+ sample[2] = -1;
+
+ if (srcBytes > 1)
+ {
+ sample[2] = *srcP++;
+ }
+
+ switch (state)
+ {
+ case kBeginRun: // Determine whether or not to pack the bytes
+ if (sample[2] == sample[0] && sample[2] == sample[1])
+ {
+ state = kRepeatRun;
+ opCount = -2;
+ }
+ else if (sample[0] != -1)
+ {
+ state = kCopyRun;
+ opCount = 0;
+ opP = dstP++;
+ *dstP++ = (uint8) sample[0];
+ }
+ break;
+
+ case kRepeatRun: // We're packing bytes
+ if (sample[2] == sample[1])
+ {
+ --opCount;
+
+ if (opCount > -127)
+ {
+ break;
+ }
+
+ sample[2] = -1;
+ }
+
+ sample[1] = -1;
+ *dstP++ = (uint8) opCount;
+ *dstP++ = (uint8) sample[0];
+ state = kBeginRun;
+ break;
+
+ case kCopyRun: // We're copying bytes
+ if (sample[0] != sample[1] || sample[0] != sample[2])
+ {
+ *dstP++ = (uint8) sample[0];
+ ++opCount;
+
+ if (opCount >= 127)
+ {
+ *opP = (uint8) opCount;
+ state = kBeginRun;
+ break;
+ }
+ }
+ else
+ {
+ *opP = (uint8) opCount;
+ state = kRepeatRun;
+ opCount = -2;
+ }
+ break;
+
+ default:
+ EmAssert (false);
+ break;
+ }
+ }
+
+ if (state == kCopyRun)
+ {
+ *opP = (uint8) opCount;
+ }
+
+ *srcPP = (void*) srcP;
+ *dstPP = (void*) dstP;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: RunLengthDecode
+ *
+ * DESCRIPTION: Decode the data packed by RunLengthEncode.
+ *
+ * PARAMETERS: srcPP -
+ *
+ * dstPP -
+ *
+ * dstBytes -
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void RunLengthDecode (void** srcPP, void** dstPP, long srcBytes, long dstBytes)
+{
+ UNUSED_PARAM(srcBytes)
+
+ int8* srcP = (int8*) *srcPP;
+ int8* dstP = (int8*) *dstPP;
+ int8* limitP = dstP + dstBytes;
+
+ while (dstP < limitP)
+ {
+ int op = *srcP++;
+ if (op == -128)
+ {
+ // Nothing
+ }
+ else if (op >= 0)
+ {
+ int count = op + 1;
+ do
+ {
+ *dstP++ = *srcP++;
+ } while (--count);
+ }
+ else
+ {
+ int count = 1 - op;
+ uint8 fillData = *srcP++;
+ do
+ {
+ *dstP++ = fillData;
+ } while (--count);
+ }
+ }
+
+ *srcPP = (void*) srcP;
+ *dstPP = (void*) dstP;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: RunLengthWorstSize
+ *
+ * DESCRIPTION: Calculate the largest buffer needed when packing a
+ * buffer "srcBytes" long. The algorithm is based on
+ * that found in Macintosh Technote 1023.
+ *
+ * PARAMETERS: srcBytes - number of bytes in the buffer to be encoded.
+ *
+ * RETURNED: Largest buffer size needed to encode source buffer.
+ *
+ ***********************************************************************/
+
+long RunLengthWorstSize (long srcBytes)
+{
+ long maxDestBytes = (srcBytes + (srcBytes + 126) / 127);
+
+ return maxDestBytes;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GzipEncode
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: srcPP - pointer to the pointer to the source bytes. The
+ * referenced pointer gets udpated to point past the
+ * last byte included the packed output.
+ *
+ * dstPP - pointer to the pointer to the destination buffer.
+ * The referenced pointer gets updated to point past
+ * the last byte stored in the output buffer.
+ *
+ * srcBytes - length of the buffer referenced by srcPP
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void GzipEncode (void** srcPP, void** dstPP, long srcBytes, long dstBytes)
+{
+ gSrcP = *srcPP;
+ gDstP = *dstPP;
+ gSrcBytes = srcBytes;
+ gDstBytes = dstBytes;
+ gSrcOffset = 0;
+ gDstOffset = 0;
+
+ bytes_in = srcBytes; // (for gzip internal debugging)
+
+ ush attr = 0; /* ascii/binary flag */
+ ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */
+ int method;
+
+ clear_bufs ();
+
+ read_buf = &::PrvGzipReadProc;
+ write_buf_proc = &::PrvGzipWriteProc;
+
+ bi_init (NO_FILE);
+ ct_init (&attr, &method);
+ lm_init (level, &deflate_flags);
+
+ deflate ();
+
+ // Perform a put_byte(0) to pad out the
+ // compressed buffer. gzip apparently can skid off the
+ // end of the compressed data when inflating it, so we need
+ // an extra zero.
+
+ put_byte (0);
+
+ flush_outbuf ();
+
+ *srcPP = ((char*) gSrcP) + gSrcOffset;
+ *dstPP = ((char*) gDstP) + gDstOffset;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GzipDecode
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: srcPP -
+ *
+ * dstPP -
+ *
+ * dstBytes -
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+void GzipDecode (void** srcPP, void** dstPP, long srcBytes, long dstBytes)
+{
+ gSrcP = *srcPP;
+ gDstP = *dstPP;
+ gSrcBytes = srcBytes;
+ gDstBytes = dstBytes;
+ gSrcOffset = 0;
+ gDstOffset = 0;
+
+ clear_bufs ();
+
+ read_buf = &::PrvGzipReadProc;
+ write_buf_proc = &::PrvGzipWriteProc;
+
+ inflate ();
+
+ *srcPP = ((char*) gSrcP) + gSrcOffset;
+ *dstPP = ((char*) gDstP) + gDstOffset;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GzipWorstSize
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: srcBytes - number of bytes in the buffer to be encoded.
+ *
+ * RETURNED: Largest buffer size needed to encode source buffer.
+ *
+ ***********************************************************************/
+
+long GzipWorstSize (long srcBytes)
+{
+ long maxDestBytes = srcBytes * 2;
+
+ return maxDestBytes;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: PrvGzipReadProc
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: .
+ *
+ ***********************************************************************/
+
+int PrvGzipReadProc (char* buf, unsigned size)
+{
+ if (gSrcOffset == gSrcBytes)
+ return EOF;
+
+ if (size > (unsigned) (gSrcBytes - gSrcOffset))
+ size = gSrcBytes - gSrcOffset;
+
+ if (size > 0)
+ {
+ memcpy (buf, ((char*) gSrcP) + gSrcOffset, size);
+ gSrcOffset += size;
+ }
+
+ return size;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: PrvGzipWriteProc
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: .
+ *
+ ***********************************************************************/
+
+int PrvGzipWriteProc (char* buf, unsigned size)
+{
+ if (gDstOffset == gDstBytes)
+ return EOF;
+
+ if (size > (unsigned) (gDstBytes - gDstOffset))
+ size = gDstBytes - gDstOffset;
+
+ if (size > 0)
+ {
+ memcpy (((char*) gDstP) + gDstOffset, buf, size);
+ gDstOffset += size;
+ }
+
+ return size;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: StackCrawlStrings
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: .
+ *
+ ***********************************************************************/
+
+void StackCrawlStrings (const EmStackFrameList& stackCrawl, StringList& stackCrawlStrings)
+{
+ EmStackFrameList::const_iterator iter = stackCrawl.begin ();
+ while (iter != stackCrawl.end ())
+ {
+ // Get the function name.
+
+ char funcName[256] = {0};
+ ::FindFunctionName (iter->fAddressInFunction, funcName, NULL, NULL, 255);
+
+ // If we can't find the name, dummy one up.
+
+ if (strlen (funcName) == 0)
+ {
+ sprintf (funcName, "<Unknown @ 0x%08lX>", iter->fAddressInFunction);
+ }
+
+ stackCrawlStrings.push_back (string (funcName));
+
+ ++iter;
+ }
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: StackCrawlString
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: .
+ *
+ * RETURNED: .
+ *
+ ***********************************************************************/
+
+string StackCrawlString (const EmStackFrameList& stackCrawl, long maxLen, Bool includeFrameSize, emuptr oldStackLow)
+{
+ StringList strings;
+ ::StackCrawlStrings (stackCrawl, strings);
+
+ string stackCrawlString;
+
+ EmStackFrameList::const_iterator iter = stackCrawl.begin ();
+ StringList::const_iterator s_iter = strings.begin ();
+
+ while (iter != stackCrawl.end ())
+ {
+ // Catenate the function name to the built-up string.
+
+ if (iter != stackCrawl.begin ())
+ {
+ stackCrawlString += ", ";
+ }
+
+ stackCrawlString += *s_iter;
+
+ if (includeFrameSize)
+ {
+ // Get the stack size used by the function.
+
+ char stackSize[20];
+ sprintf (stackSize, "%ld", iter->fA6 - oldStackLow);
+
+ stackCrawlString += string ("(") + string (stackSize) + ")";
+ }
+
+ // If the string looks long enough, stop.
+
+ if (maxLen > 0 && (long) stackCrawlString.size () > maxLen)
+ {
+ stackCrawlString += "...";
+ break;
+ }
+
+ oldStackLow = iter->fA6;
+
+ ++iter;
+ ++s_iter;
+ }
+
+ return stackCrawlString;
+}
+
+#pragma mark -
+
+static int kBitCount[16] =
+{
+ 0, 1, 1, 2,
+ 1, 2, 2, 3,
+ 1, 2, 2, 3,
+ 2, 3, 3, 4
+};
+
+int CountBits (uint32 v)
+{
+ return kBitCount[ (v >> 0) & 0x0F ] +
+ kBitCount[ (v >> 4) & 0x0F ] +
+ kBitCount[ (v >> 8) & 0x0F ] +
+ kBitCount[ (v >> 12) & 0x0F ];
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: NextPowerOf2
+ *
+ * DESCRIPTION: Calculates the next power of two above the given number
+ * If the given number is already a power of two, it is
+ * returned.
+ *
+ * PARAMETERS: n - probe number
+ *
+ * RETURNED: Next power of two above the probe number, or the number
+ * itself if it is a power of two.
+ *
+ ***********************************************************************/
+
+// Seven implementations! No waiting!
+
+uint32 NextPowerOf2 (uint32 n)
+{
+ // Smear down the upper 1 bit to all bits lower than it.
+
+ uint32 n2 = n;
+
+ n2 |= n2 >> 1;
+ n2 |= n2 >> 2;
+ n2 |= n2 >> 4;
+ n2 |= n2 >> 8;
+ n2 |= n2 >> 16;
+
+ // Now use itself to clear all the lower bits.
+
+ n2 &= ~(n2 >> 1);
+
+ // If n2 ends up being the same as what we started with, keep it.
+ // Otherwise, we need to bump it by a factor of two (round up).
+
+ if (n2 != n)
+ n2 <<= 1;
+
+ return n2;
+}
+
+
+#if 0
+uint32 NextPowerOf2 (uint32 n)
+{
+ uint32 startn = n;
+ uint32 prevn = 0;
+
+ while (n) // Loop until we're out of bits
+ {
+ prevn = n; // Remember what we're starting with. When "n"
+ // reaches zero, prevn will hold the previous value,
+ // which will have a single bit in it. Since we're
+ // whacking off bits from the bottom, this will be
+ // the highest bit.
+ n &= n - 1; // Mask off the low bit
+ }
+
+ // If prevn ends up being the same as what we started with, keep it.
+ // Otherwise, we need to bump it by a factor of two.
+
+ if (prevn != startn)
+ prevn <<= 1;
+
+ return prevn;
+}
+#endif
+
+
+#if 0
+ // This was my own first attempt. Pretty lame...
+
+uint32 NextPowerOf2 (uint32 x)
+{
+ // Figure out the next power-of-2 higher than or equal to 'x'. We do
+ // this by continually shifting x to the left until we get a '1' in
+ // the upper bit. At the same time, we shift 0x80000000 to the right.
+ // When we find that '1' in the upper bit, the shifted 0x80000000
+ // pattern should hold our power-of-two.
+ //
+ // That approach is good for finding the next power-of-2 higher than
+ // a given value, so in order to deal with the 'or equal to' part
+ // of the task, we decrement x by 1 before embarking on this adventure.
+ //
+ // This function'll fail for x == 0 or x == 1, as well x > 0x80000000,
+ // so handle those cases up front:
+
+ if (x > 0x80000000)
+ return -1;
+
+ if (x == 0 || x == 1)
+ return x;
+
+ --x;
+
+ unsigned long result = 0x80000000;
+ while (((x <<= 1) & 0x80000000) == 0) // check for the highest set bit.
+ result >>= 1;
+
+ return result;
+}
+#endif
+
+#if 0
+ // This one was posted to the net. Seems most reasonable.
+ // Fails when n == 0.
+uint32 HighBitNumber (uint32 n)
+{
+ uint32 i = (n & 0xffff0000) ? 16 : 0;
+
+ if ((n >>= i) & 0xff00)
+ {
+ i |= 8;
+ n >>= 8;
+ }
+
+ if (n & 0xf0)
+ {
+ i |= 4;
+ n >>= 4;
+ }
+
+ if (n & 0xc)
+ {
+ i |= 2;
+ n >>= 2;
+ }
+
+ return (i | (n >> 1));
+}
+#endif
+
+#if 0
+ // This one was posted to the net. Uses a loop; not quite as effecient.
+ // Seems pretty buggy, since it doesn't work for x == 0, 1, or 2.
+uint32 HighBitNumber (uint32 x)
+{
+ unsigned long mask=2, numBits=1;
+ while (mask < x)
+ {
+ mask += mask;
+ numBits++;
+ }
+
+ return numBits;
+}
+#endif
+
+#if 0
+ // This one was posted to the net. Makes up to 5 comparisons, which is
+ // more than the one we're using.
+uint32 HighBitNumber (uint32 x)
+{
+#define hi_bit(n)\
+ ((n)>=1<<16?(n)>=1<<24?(n)>=1<<28?(n)>=1<<30?(n)>=1<<31?31:30:(n)>=1<<29?\
+ 29:28:(n)>=1<<26?(n)>=1<<27?27:26:(n)>=1<<25?25:24:(n)>=1<<20?(n)>=1<<22?\
+ (n)>=1<<23?23:22:(n)>=1<<21?21:20:(n)>=1<<18?(n)>=1<<19?19:18:(n)>=1<<17?\
+ 17:16:(n)>=1<<8?(n)>=1<<12?(n)>=1<<14?(n)>=1<<15?15:14:(n)>=1<<13?13:12:(\
+ n)>=1<<10?(n)>=1<<11?11:10:(n)>=1<<9?9:8:(n)>=1<<4?(n)>=1<<6?(n)>=1<<7?7:\
+ 6:(n)>=1<<5?5:4:(n)>=1<<2?(n)>=1<<3?3:2:(n)>=1<<1?1:(n)>=1<<0?0:-1)
+
+ return hi_bit (x);
+}
+#endif
+
+#if 0
+ // This one was posted to the net (by the same guy posting that macro).
+ // Pretty neat until that divide by 37.
+uint32 HighBitNumber (uint32 x)
+{
+ static const int t[] =
+ {
+ -1, 0, 25, 1, 22, 26, 31, 2, 15, 23, 29, 27, 10, -1, 12, 3, 6, 16,
+ -1, 24, 21, 30, 14, 28, 9, 11, 5, -1, 20, 13, 8, 4, 19, 7, 18, 17
+ };
+
+ return t[(n |= n >> 1, n |= n >> 2, n |= n >> 4, n |= n >> 8, n |= n >> 16) % 37];
+}
+#endif
+
+
+/***********************************************************************
+ *
+ * FUNCTION: DateToDays
+ *
+ * DESCRIPTION: Convert a year, month, and day into the number of days
+ * since 1/1/1904.
+ *
+ * Parameters are not checked for valid dates, so it's
+ * possible to feed in things like March 35, 1958. This
+ * function also assumes that year is at least 1904, and
+ * will only work up until 2040 or so.
+ *
+ * PARAMETERS: year - full year
+ *
+ * month - 1..12
+ *
+ * day - 1..31
+ *
+ * RETURNED: Number of days since 1/1/1904.
+ *
+ ***********************************************************************/
+
+uint32 DateToDays (uint32 year, uint32 month, uint32 day)
+{
+ static const int month2days[] =
+ {
+ 0, 31, 59, 90, 120, 151,
+ 181, 212, 243, 273, 304, 334
+ };
+
+
+ // Normalize the values.
+
+ year -= 1904;
+ month -= 1;
+ day -= 1;
+
+ // Not counting any possible leap-day in the current year, figure out
+ // the number of days between now and 1/1/1904.
+
+ const uint32 kNumDaysInLeapCycle = 4 * 365 + 1;
+
+ uint32 days = day + month2days[month] +
+ (year * kNumDaysInLeapCycle + 3) / 4;
+
+ // Now add in this year's leap-day, if there is one.
+
+ if ((month >= 2) && ((year & 3) == 0))
+ days++;
+
+ return days;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GetLibraryName
+ *
+ * DESCRIPTION:
+ *
+ * PARAMETERS: none
+ *
+ * RETURNED: The libraries name, or an empty string if the library
+ * could not be found.
+ *
+ ***********************************************************************/
+
+string GetLibraryName (uint16 refNum)
+{
+ if (refNum == sysInvalidRefNum)
+ return string();
+
+ CEnableFullAccess munge; // Remove blocks on memory access.
+
+ /*
+ The System Library Table (sysLibTableP) is an array of
+ sysLibTableEntries entries. Each entry has the following
+ format:
+
+ Ptr* dispatchTblP; // pointer to library dispatch table
+ void* globalsP; // Library globals
+ LocalID dbID; // database id of the library
+ MemPtr codeRscH; // library code resource handle for RAM-based libraries
+
+ The latter two fields are present only in Palm OS 2.0 and
+ later. So our first steps are to (a) get the pointer to
+ the array, (b) make sure that the index into the array (the
+ refNum passed as the first parameter to all library calls)
+ is within range, (c) get a pointer to the right entry,
+ taking into account the Palm OS version, and (d) getting the
+ dispatchTblP field.
+
+ The "library dispatch table" is an array of 16-bit offsets. The
+ values are all relative to the beginning of the table (dispatchTblP).
+ The first entry in the array corresponds to the library name. All
+ subsequent entries are offsets to the various library functions,
+ starting with the required four: sysLibTrapOpen, sysLibTrapClose,
+ sysLibTrapSleep, and sysLibTrapWake.
+ */
+
+ emuptr sysLibTableP = EmLowMem_GetGlobal (sysLibTableP);
+ UInt16 sysLibTableEntries = EmLowMem_GetGlobal (sysLibTableEntries);
+
+ if (sysLibTableP == EmMemNULL)
+ {
+ // !!! No library table!
+ EmAssert (false);
+ return string();
+ }
+
+ if (refNum >= sysLibTableEntries)
+ {
+ if (refNum != 0x0666)
+ {
+ // !!! RefNum out of range!
+ EmAssert (false);
+ }
+
+ return string();
+ }
+
+ emuptr libEntry;
+ emuptr dispatchTblP;
+
+ if (EmPatchState::OSMajorVersion () > 1)
+ {
+ libEntry = sysLibTableP + refNum * sizeof (SysLibTblEntryType);
+ dispatchTblP = EmMemGet32 (libEntry + offsetof (SysLibTblEntryType, dispatchTblP));
+ }
+ else
+ {
+ libEntry = sysLibTableP + refNum * sizeof (SysLibTblEntryTypeV10);
+ dispatchTblP = EmMemGet32 (libEntry + offsetof (SysLibTblEntryTypeV10, dispatchTblP));
+ }
+
+ // The first entry in the table is always the offset from the
+ // start of the table to the library name. Use this information
+ // get the library name.
+
+ int16 offset = EmMemGet16 (dispatchTblP + LibTrapIndex (sysLibTrapName) * 2);
+ emuptr libNameP = dispatchTblP + offset;
+
+ char libName[256];
+ EmMem_strcpy (libName, libNameP);
+
+ return string (libName);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GetSystemCallContext
+ *
+ * DESCRIPTION: .
+ *
+ * PARAMETERS: None.
+ *
+ * RETURNED: Nothing.
+ *
+ ***********************************************************************/
+
+Bool GetSystemCallContext (emuptr pc, SystemCallContext& context)
+{
+ context.fPC = pc;
+
+ // Determine how the system function is being called. There are two ways:
+ //
+ // * Via SYS_TRAP macro:
+ //
+ // TRAP $F
+ // DC.W $Axxx
+ //
+ // * Via SYS_TRAP_FAST macro:
+ //
+ // MOVE.L struct(LowMemType.fixed.globals.sysDispatchTableP), A1
+ // MOVE.L ((trapNum-sysTrapBase)*4)(A1), A1
+ // JSR (A1) ; opcode == 0x4e91
+ //
+ // The PC is current pointing to either the TRAP $F or the JSR (A1),
+ // so we can look at the opcode to determine how we got here.
+
+ uint8* realMem = EmMemGetRealAddress (pc);
+ uint16 opcode = EmMemDoGet16 (realMem);
+
+ context.fViaTrap = opcode == (m68kTrapInstr + sysDispatchTrapNum);
+ context.fViaJsrA1 = opcode == (0x4e91);
+
+
+ if (context.fViaTrap)
+ {
+ // Not all development systems generate the correct dispatch
+ // numbers; some leave off the preceding "A". Make sure it's
+ // set so that we can recognize it as a trap dispatch number.
+ // (This code is here specifically so that the profiling routines
+ // will work, which check for trap numbers masquerading as function
+ // addresses by checking to see if they are in the sysTrapBase range.)
+
+ context.fTrapWord = EmMemGet16 (pc + 2) | sysTrapBase;
+ context.fNextPC = pc + 4;
+ }
+ else if (context.fViaJsrA1)
+ {
+ context.fTrapWord = (EmMemGet16 (pc - 2) / 4) | sysTrapBase;
+ context.fNextPC = pc + 2;
+ }
+ else
+ {
+ EmAssert (false);
+ return false;
+ }
+
+ if (::IsSystemTrap (context.fTrapWord))
+ {
+ context.fTrapIndex = SysTrapIndex (context.fTrapWord);
+ context.fExtra = m68k_dreg (regs, 2);
+ }
+ else
+ {
+ context.fTrapIndex = LibTrapIndex (context.fTrapWord);
+ context.fExtra = EmMemGet16 (m68k_areg (regs, 7));
+ }
+
+ EmAssert ((context.fTrapWord >= sysTrapBase) && (context.fTrapWord < sysTrapBase + 0x1000));
+
+ try
+ {
+ context.fError = 0;
+ context.fDestPC1 = ::GetFunctionAddress (context.fTrapWord, context.fExtra, false);
+ context.fDestPC2 = ::GetFunctionAddress (context.fTrapWord, context.fExtra, true);
+ }
+ catch (EmUnimplementedFunctionException& e)
+ {
+ context.fDestPC1 = EmMemNULL;
+ context.fDestPC2 = EmMemNULL;
+ context.fError = kError_UnimplementedTrap;
+ context.fLibIndex = e.fLibIndex;
+ return false;
+ }
+ catch (EmInvalidRefNumException& e)
+ {
+ context.fDestPC1 = EmMemNULL;
+ context.fDestPC2 = EmMemNULL;
+ context.fError = kError_InvalidLibraryRefNum;
+ context.fLibIndex = e.fLibIndex;
+ return false;
+ }
+
+ return true;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GetHostTime
+ *
+ * DESCRIPTION: Returns the current time in hours, minutes, and seconds.
+ *
+ * PARAMETERS: none
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+void GetHostTime (long* hour, long* min, long* sec)
+{
+ time_t t;
+ struct tm tm;
+
+ time (&t);
+ tm = *localtime (&t);
+
+ *hour = tm.tm_hour; // 0...23
+ *min = tm.tm_min; // 0...59
+ *sec = tm.tm_sec; // 0...59
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GetHostDate
+ *
+ * DESCRIPTION: Returns years since 1900, month as 0-11, and day as 1-31
+ *
+ * PARAMETERS: none
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+void GetHostDate (long* year, long* month, long* day)
+{
+ time_t t;
+ struct tm tm;
+
+ time (&t);
+ tm = *localtime (&t);
+
+ *year = tm.tm_year + 1900; // 1904...2040
+ *month = tm.tm_mon + 1; // 1...12
+ *day = tm.tm_mday; // 1...31
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: StartsWith
+ *
+ * DESCRIPTION: Determine if a string starts with the given pattern.
+ *
+ * PARAMETERS: s - string to test.
+ *
+ * p - pattern to test with.
+ *
+ * RETURNED: True if "s" starts with "p".
+ *
+ ***********************************************************************/
+
+Bool StartsWith (const char* s, const char* p)
+{
+ if (strlen (s) < strlen (p))
+ return false;
+
+ return (_strnicmp (s, p, strlen (p)) == 0);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: EndsWith
+ *
+ * DESCRIPTION: Determine if a string end with the given pattern.
+ *
+ * PARAMETERS: s - string to test.
+ *
+ * p - pattern to test with.
+ *
+ * RETURNED: True if "s" ends with "p".
+ *
+ ***********************************************************************/
+
+Bool EndsWith (const char* s, const char* p)
+{
+ if (strlen (s) < strlen (p))
+ return false;
+
+ const char* buffer = s + strlen(s) - strlen(p);
+ return (_stricmp (buffer, p) == 0);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: Strip
+ *
+ * DESCRIPTION: Remove leading or trailing instances of the given
+ * character(s).
+ *
+ * PARAMETERS: s - string to modify.
+ *
+ * ch - character(s) to remove.
+ *
+ * leading - true if leading characters should be removed.
+ *
+ * trailing - true if trailing characters should be removed.
+ *
+ * RETURNED: Modified string.
+ *
+ ***********************************************************************/
+
+string Strip (const char* s, const char* ch, Bool leading, Bool trailing)
+{
+ string result (s);
+ string chars (ch);
+
+ if (leading)
+ {
+ // Iterate over the string, looking to see if the leading character
+ // is in the given character set.
+
+ string::iterator iter = result.begin ();
+ while (iter != result.end ())
+ {
+ if (chars.find (*iter) == string::npos)
+ break;
+
+ result.erase (iter);
+ iter = result.begin ();
+ }
+ }
+
+ if (trailing)
+ {
+ // Iterate over the string, looking to see if the trailing character
+ // is in the given character set.
+
+ string::reverse_iterator iter = result.rbegin ();
+ while (iter != result.rend ())
+ {
+ if (chars.find (*iter) == string::npos)
+ break;
+
+ result.erase (iter.base () - 1);
+ iter = result.rbegin ();
+ }
+ }
+
+ return result;
+}
+string Strip (const string& s, const char* ch, Bool leading, Bool trailing)
+{
+ return ::Strip (s.c_str (), ch, leading, trailing);
+}
+
+/***********************************************************************
+ *
+ * FUNCTION: ReplaceString
+ *
+ * DESCRIPTION: Replace all occurances of one string inside of a second
+ * string with a third string.
+ *
+ * PARAMETERS: source - the string on which the replacements are to
+ * be made.
+ *
+ * pattern - substring to search for.
+ *
+ * replacement - string with which to replace the patterm.
+ *
+ * RETURNED: The changed string.
+ *
+ ***********************************************************************/
+
+string ReplaceString (const string& source,
+ const string& pattern,
+ const string& replacement)
+{
+ string result (source);
+ string::size_type pos = 0;
+
+ for (;;)
+ {
+ pos = result.find (pattern, pos);
+ if (pos == string::npos)
+ break;
+ result.replace (pos, pattern.size (), replacement);
+ pos += replacement.size ();
+ }
+
+ return result;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: PrvInsertString
+ *
+ * DESCRIPTION: Insert one string into the middle of another. The
+ * buffer containing the string being modified should be
+ * large enough to hold the resulting string.
+ *
+ * PARAMETERS: dest - buffer containing the string to receive the
+ * inserted text.
+ *
+ * insertBefore - character index at which the inserted
+ * text should appear. Assumed to be [0, strlen(dest)].
+ *
+ * src - text to be inserted.
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+static void PrvInsertString (char* dest, int insertBefore, const char* src)
+{
+ size_t destLen = strlen (dest);
+ size_t srcLen = strlen (src);
+
+ // Move down the part of the string after the insertion point. Don't
+ // use strcpy or memcpy, as we're dealing with an overlapping range.
+ // Add 1 to the range length to get the terminating NULL.
+
+ memmove (dest + insertBefore + srcLen, dest + insertBefore, destLen - insertBefore + 1);
+
+ // Insert the new string. *Don't* copy the NULL!
+
+ memmove (dest + insertBefore, src, srcLen);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: FormatInteger
+ *
+ * DESCRIPTION: Convert an integral value into a formatted string,
+ * adding thousands seperators as necessary.
+ *
+ * PARAMETERS: dest - buffer for the outputted string
+ *
+ * integer - input value
+ *
+ * RETURNED: nothing
+ *
+ ***********************************************************************/
+
+void FormatInteger (char* dest, uint32 integer)
+{
+ sprintf (dest, "%ld", integer);
+
+ // Get the thousands separator character(s).
+
+ struct lconv* locale_data = localeconv ();
+ char* thousands_sep = locale_data->thousands_sep;
+
+ if (strlen (thousands_sep) == 0)
+ {
+ thousands_sep = ",";
+ }
+
+ // Insert the thousands separator(s).
+
+ // Divide by three to get the number of 3 digit groupings remaining
+ // (subtracting one to get the math to come out right)
+ //
+ // 1 -> 0
+ // 2 -> 0
+ // 3 -> 0
+ // 4 -> 1
+ // 5 -> 1
+ // 6 -> 1
+ // 7 -> 2
+ // 8 -> 2
+ // 9 -> 2
+ // 10 -> 3
+ //
+ // etc...
+
+ int numCommas = (strlen (dest) - 1) / 3;
+
+ // Special case the stupid rule about not putting a comma
+ // in a number like xxxx.
+
+ if (strlen (dest) <= 4)
+ {
+ numCommas = 0;
+ }
+
+ for (int ii = 1; ii <= numCommas; ++ii)
+ {
+ // Back up four for every comma (skip past every ",xxx" pattern).
+
+ ::PrvInsertString (dest, strlen (dest) + 1 - (4 * ii), thousands_sep);
+ }
+}
+
+string FormatInteger (uint32 integer)
+{
+ // Format the integer as a plain string.
+
+ strstream stream;
+
+ stream << integer;
+
+ string result (stream.str (), stream.pcount ());
+
+ // Unfreeze the stream, or else its storage will be leaked.
+
+ stream.freeze (false);
+
+ // Get the thousands separator character(s).
+
+ struct lconv* locale_data = localeconv ();
+ char* thousands_sep = locale_data->thousands_sep;
+
+ if (strlen (thousands_sep) == 0)
+ {
+ thousands_sep = ",";
+ }
+
+ // Insert the thousands separator(s).
+
+ // Divide by three to get the number of 3 digit groupings remaining
+ // (subtracting one to get the math to come out right)
+ //
+ // 1 -> 0
+ // 2 -> 0
+ // 3 -> 0
+ // 4 -> 1
+ // 5 -> 1
+ // 6 -> 1
+ // 7 -> 2
+ // 8 -> 2
+ // 9 -> 2
+ // 10 -> 3
+ //
+ // etc...
+
+ int numCommas = (result.size () - 1) / 3;
+
+ // Special case the stupid rule about not putting a comma
+ // in a number like xxxx.
+
+ if (result.size () <= 4)
+ {
+ numCommas = 0;
+ }
+
+ for (int ii = 1; ii <= numCommas; ++ii)
+ {
+ // Back up four for every comma (skip past every ",xxx" pattern).
+
+ result.insert (result.size () + 1 - (4 * ii), thousands_sep);
+ }
+
+ return result;
+}
+
+
+string FormatElapsedTime (uint32 mSecs)
+{
+ // Get hours, minutes, and seconds.
+
+ const long kMillisecondsPerSecond = 1000;
+ const long kSecondsPerMinute = 60;
+ const long kMinutesPerHour = 60;
+
+ const long kMillisecondsPerMinute = kMillisecondsPerSecond * kSecondsPerMinute;
+ const long kMillisecondsPerHour = kMillisecondsPerMinute * kMinutesPerHour;
+
+ long hours = mSecs / kMillisecondsPerHour; mSecs -= hours * kMillisecondsPerHour;
+ long minutes = mSecs / kMillisecondsPerMinute; mSecs -= minutes * kMillisecondsPerMinute;
+ long seconds = mSecs / kMillisecondsPerSecond; mSecs -= seconds * kMillisecondsPerSecond;
+
+ // Format them into a string.
+
+ char formattedTime[20];
+ sprintf (formattedTime, "%ld:%02ld:%02ld", hours, minutes, seconds);
+
+ return string (formattedTime);
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: LaunchCmdToString
+ *
+ * DESCRIPTION: Convert the given launch command (the command that's
+ * passed to PilotMain in a Palm OS application) into a
+ * text form suitable for displaying to a user.
+ *
+ * PARAMETERS: cmd - the launch command (from SystemMgr.h)
+ *
+ * RETURNED: A pointer to a string representing the command. If
+ * the command is unrecognized (probably because this
+ * function is out-of-date and needs to be updated for
+ * newly-added commands), a default string containing
+ * the command number is returned.
+ *
+ ***********************************************************************/
+
+const char* LaunchCmdToString (UInt16 cmd)
+{
+#undef DOCASE
+#define DOCASE(name) \
+ case name: \
+ return #name;
+
+ switch (cmd)
+ {
+ DOCASE (sysAppLaunchCmdNormalLaunch)
+ DOCASE (sysAppLaunchCmdFind)
+ DOCASE (sysAppLaunchCmdGoTo)
+ DOCASE (sysAppLaunchCmdSyncNotify)
+ DOCASE (sysAppLaunchCmdTimeChange)
+ DOCASE (sysAppLaunchCmdSystemReset)
+ DOCASE (sysAppLaunchCmdAlarmTriggered)
+ DOCASE (sysAppLaunchCmdDisplayAlarm)
+ DOCASE (sysAppLaunchCmdCountryChange)
+ DOCASE (sysAppLaunchCmdSyncRequestLocal)
+ DOCASE (sysAppLaunchCmdSaveData)
+ DOCASE (sysAppLaunchCmdInitDatabase)
+ DOCASE (sysAppLaunchCmdSyncCallApplicationV10)
+ DOCASE (sysAppLaunchCmdPanelCalledFromApp)
+ DOCASE (sysAppLaunchCmdReturnFromPanel)
+ DOCASE (sysAppLaunchCmdLookup)
+ DOCASE (sysAppLaunchCmdSystemLock)
+ DOCASE (sysAppLaunchCmdSyncRequestRemote)
+ DOCASE (sysAppLaunchCmdHandleSyncCallApp)
+ DOCASE (sysAppLaunchCmdAddRecord)
+ DOCASE (sysSvcLaunchCmdSetServiceID)
+ DOCASE (sysSvcLaunchCmdGetServiceID)
+ DOCASE (sysSvcLaunchCmdGetServiceList)
+ DOCASE (sysSvcLaunchCmdGetServiceInfo)
+ DOCASE (sysAppLaunchCmdFailedAppNotify)
+ DOCASE (sysAppLaunchCmdEventHook)
+ DOCASE (sysAppLaunchCmdExgReceiveData)
+ DOCASE (sysAppLaunchCmdExgAskUser)
+ DOCASE (sysDialLaunchCmdDial)
+ DOCASE (sysDialLaunchCmdHangUp)
+ DOCASE (sysSvcLaunchCmdGetQuickEditLabel)
+ DOCASE (sysAppLaunchCmdURLParams)
+ DOCASE (sysAppLaunchCmdNotify)
+ DOCASE (sysAppLaunchCmdOpenDB)
+ DOCASE (sysAppLaunchCmdAntennaUp)
+ DOCASE (sysAppLaunchCmdGoToURL)
+ }
+
+ static char buffer[20];
+ sprintf (buffer, "#%ld", (long) cmd);
+ return buffer;
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: GetMemoryList
+ *
+ * DESCRIPTION: Return the list of items that should appear in the
+ * various listboxes and menus that allow the user to
+ * select a memory size. The list is returned as a
+ * collection of pairs. The first element of the pair is
+ * the memory size (in K), and the second element of the
+ * pair is the same thing in textform, suitable for
+ * displaying to the user.
+ *
+ * PARAMETERS: memoryList - reference to the collection to receive
+ * the results.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void GetMemoryTextList (MemoryTextList& memoryList)
+{
+ memoryList.push_back (make_pair (RAMSizeType (128), string ("128K")));
+ memoryList.push_back (make_pair (RAMSizeType (256), string ("256K")));
+ memoryList.push_back (make_pair (RAMSizeType (512), string ("512K")));
+ memoryList.push_back (make_pair (RAMSizeType (1024), string ("1024K")));
+ memoryList.push_back (make_pair (RAMSizeType (2048), string ("2048K")));
+ memoryList.push_back (make_pair (RAMSizeType (4096), string ("4096K")));
+ memoryList.push_back (make_pair (RAMSizeType (8192), string ("8192K")));
+ memoryList.push_back (make_pair (RAMSizeType (16384), string ("16,384K")));
+}
+
+
+/***********************************************************************
+ *
+ * FUNCTION: MyAssertFailed
+ *
+ * DESCRIPTION: Called by the EmAssert macro if NDEBUG is defined.
+ * EmAssert and MyAssertFailed are moral replacements for
+ * the assert macro of the Standard C Library and the
+ * underlying function it calls. However, those facilities
+ * don't necessarily work the way we'd like. In
+ * particular, on Linux, they will abort the application.
+ * Sometimes, we'd like the ability to continue application
+ * execution.
+ *
+ * PARAMETERS: exp - expression that evaluate to false.
+ * file - name of file containing the expression.
+ * line - line number in the file containing the expression.
+ *
+ * RETURNED: Nothing
+ *
+ ***********************************************************************/
+
+void MyAssertFailed (const char* expr, const char* file, unsigned int line)
+{
+ char message[2000];
+
+ sprintf (message, "Assertion Failure: Expression: \"%s\", File: %s, Line: %d",
+ expr, file, line);
+
+ LogDump ();
+
+ Platform::Debugger (message);
+}