#include <sl_words.h>
 #include <libclite.h>
 
-void  putchar(int ch)
+int putchar(int ch)
 {
   if ((ch == '\r') || (ch == '\n')) CallMethod(0, 0, SLWordsIH, "slw_cr");
   else CallMethod(1, 0, SLWordsIH, "slw_emit", ch);
+  
+  return ch;
 }
 
                return 1;
 }
 
+//
+//  BinaryUnicodeCompare - Compare two Unicode strings; produce a relative ordering
+//  Compared using a 16-bit binary comparison (no case folding)
+//
+int32_t BinaryUnicodeCompare (u_int16_t * str1, u_int32_t length1,
+                             u_int16_t * str2, u_int32_t length2)
+{
+       register u_int16_t c1, c2;
+       int32_t bestGuess;
+       u_int32_t length;
+
+       bestGuess = 0;
+
+       if (length1 < length2) {
+               length = length1;
+               --bestGuess;
+       } else if (length1 > length2) {
+               length = length2;
+               ++bestGuess;
+       } else {
+               length = length1;
+       }
+
+       while (length--) {
+               c1 = *(str1++);
+               c2 = *(str2++);
+
+               if (c1 > c2)
+                       return (1);
+               if (c1 < c2)
+                       return (-1);
+       }
+
+       return (bestGuess);
+}
 
 /*
  * UTF-8 (UCS Transformation Format)
 
 
 #define kNumPartInfos  (16)
 static PartInfo gParts[kNumPartInfos];
+static char gMakeDirSpec[1024];
 
 // Private function prototypes
 long LookupPartition(char *devSpec);
 {
   long ret, index = 0;
   char *curName;
+
+  if (!dirSpec) {
+    long       idx, len;
+
+    len = strlen(name);
+
+    for (idx = len; idx && (name[idx] != '\\'); idx--) {}
+    idx++;
+    strncpy(gMakeDirSpec, name, idx);
+    name += idx;
+    dirSpec = gMakeDirSpec;
+  }
   
   while (1) {
     ret = GetDirEntry(dirSpec, &index, &curName, flags, time);
 
 static CICell                  gCurrentIH;
 static long long               gAllocationOffset;
 static long                    gIsHFSPlus;
+static long                    gCaseSensitive;
 static long                    gBlockSize;
 static char                    gBTreeHeaderBuffer[512];
 static BTHeaderRec             *gBTHeaders[2];
 extern long FastRelString(char *str1, char *str2);
 extern long FastUnicodeCompare(u_int16_t *uniStr1, u_int32_t len1,
                               u_int16_t *uniStr2, u_int32_t len2);
+extern long BinaryUnicodeCompare(u_int16_t *uniStr1, u_int32_t len1,
+                                u_int16_t *uniStr2, u_int32_t len2);
 extern void utf_encodestr(const u_int16_t *ucsp, int ucslen,
                          u_int8_t *utf8p, u_int32_t bufsize);
 extern void utf_decodestr(const u_int8_t *utf8p, u_int16_t *ucsp,
   
   gAllocationOffset = 0;
   gIsHFSPlus = 0;
+  gCaseSensitive = 0;
   gBTHeaders[0] = 0;
   gBTHeaders[1] = 0;
   
   Read(ih, (long)gHFSPlusHeader, kBlockSize);
   
   // Not a HFS[+] volume.
-  if (gHFSPlus->signature != kHFSPlusSigWord) return -1;
+  if ((gHFSPlus->signature != kHFSPlusSigWord) &&
+      (gHFSPlus->signature != kHFSXSigWord)) return -1;
   
   gIsHFSPlus = 1;
   gBlockSize = gHFSPlus->blockSize;
               gBTreeHeaderBuffer + btree * 256, 0);
     gBTHeaders[btree] = (BTHeaderRec *)(gBTreeHeaderBuffer + btree * 256 +
                                       sizeof(BTNodeDescriptor));
+    if ((gIsHFSPlus && btree == kBTreeCatalog) &&
+        (gBTHeaders[btree]->keyCompareType == kHFSBinaryCompare)) {
+      gCaseSensitive = 1;
+    }
   }
   
   curNode = gBTHeaders[btree]->rootNode;
     if ((searchKey->nodeName.length == 0) || (trialKey->nodeName.length == 0))
       result = searchKey->nodeName.length - trialKey->nodeName.length;
     else
-      result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
-                                 searchKey->nodeName.length,
-                                 &trialKey->nodeName.unicode[0],
-                                 trialKey->nodeName.length);
+      if (gCaseSensitive) {
+        result = BinaryUnicodeCompare(&searchKey->nodeName.unicode[0],
+                                     searchKey->nodeName.length,
+                                     &trialKey->nodeName.unicode[0],
+                                     trialKey->nodeName.length);
+      } else {
+        result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
+                                   searchKey->nodeName.length,
+                                   &trialKey->nodeName.unicode[0],
+                                   trialKey->nodeName.length);
+      }
   }
   
   return result;
 
 };
 typedef struct boot_args boot_args, *boot_args_ptr;
 
+struct compressed_kernel_header {
+  u_int32_t signature;
+  u_int32_t compress_type;
+  u_int32_t adler32;
+  u_int32_t uncompressed_size;
+  u_int32_t compressed_size;
+  u_int32_t reserved[11];
+  char      platform_name[64];
+  char      root_path[256];
+  u_int8_t  data[0];
+};
+typedef struct compressed_kernel_header compressed_kernel_header;
+
 #endif /* ! _BOOTX_BOOT_ARGS_H_ */
 
 #include <stddef.h>
 
 // ci_io.c
-extern void  putchar(int ch);
+extern int putchar(int ch);
 
 // prf.c
 extern void prf(const char *fmt, unsigned int *adx, void (*putfn_p)(),
 // mem.c
 extern void *memcpy(void *dst, const void *src, size_t len);
 extern void *memset(void *dst, int ch, size_t len);
-extern void *bcopy(void *src, void *dst, int len);
+extern void bcopy(const void *src, void *dst, size_t len);
 extern void bzero(void *dst, int len);
 
 // bsearch.c
 
 extern long gBootMode;
 extern long gBootDeviceType;
 extern long gBootFileType;
+extern char gHaveKernelCache;
 extern char gBootDevice[256];
 extern char gBootFile[256];
-extern char gRootDir[256];
 
 extern char gTempStr[4096];
 
 extern long InitConfig(void);
 extern long ParseConfigFile(char *addr);
 
+// Externs for lzss.c
+extern int decompress_lzss(u_int8_t *dst, u_int8_t *src, u_int32_t srclen);
+
 #endif /* ! _BOOTX_SL_H_ */
 
   return dst;
 }
 
-void *bcopy(void *src, void *dst, int len)
+void bcopy(const void *src, void *dst, size_t len)
 {
-  return memcpy(dst, src, len);
+  memcpy(dst, src, len);
 }
 
 void bzero(void *dst, int len)
 
 
 #define SPACE  1
 #define ZERO   2
+#define UCASE  16
 
 /*
  * Scaled down version of C Library printf.
        }
        cp = prbuf;
        do {
-               *cp++ = "0123456789abcdef"[n%b];
+               *cp++ = "0123456789abcdef0123456789ABCDEF"[(flag & UCASE) + n%b];
                n /= b;
                width++;
        } while (n);
                minwidth *= 10;
                minwidth += c - '0';
                goto again;
-       case 'x': case 'X':
+       case 'X':
+               flag |= UCASE;
+               /* fall through */
+       case 'x':
                b = 16;
                goto number;
        case 'd':
 
 
 HFILES = appleboot.h clut.h elf.h failedboot.h netboot.h
 
-CFILES = main.c macho.c device_tree.c display.c drivers.c elf.c
+CFILES = main.c macho.c device_tree.c display.c drivers.c elf.c lzss.c
 
 OTHERSRCS = Makefile.preamble Makefile Makefile.postamble
 
 
             netboot.h 
         ); 
         M_FILES = (); 
-        OTHER_LINKED = (main.c, macho.c, device_tree.c, display.c, elf.c, drivers.c); 
+        OTHER_LINKED = (main.c, macho.c, device_tree.c, display.c, elf.c, drivers.c, lzss.c); 
         OTHER_SOURCES = (Makefile.preamble, Makefile, Makefile.postamble); 
         SUBPROJECTS = (); 
         TOOLS = (); 
 
 
 static ModulePtr gModuleHead, gModuleTail;
 static TagPtr    gPersonalityHead, gPersonalityTail;
-static char      gExtensionsSpec[4096];
 static char      gDriverSpec[4096];
 static char      gFileSpec[4096];
 static char      gTempSpec[4096];
   if (gBootFileType == kNetworkDeviceType) {
     NetLoadDrivers(dirSpec);
   } else if (gBootFileType == kBlockDeviceType) {
-    strcpy(gExtensionsSpec, dirSpec);
-    strcat(gExtensionsSpec, "System\\Library\\");
-    FileLoadDrivers(gExtensionsSpec, 0);
+    FileLoadDrivers(dirSpec, 0);
   } else {
     return 0;
   }
 
--- /dev/null
+/*
+ * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/**************************************************************
+ LZSS.C -- A Data Compression Program
+***************************************************************
+    4/6/1989 Haruhiko Okumura
+    Use, distribute, and modify this program freely.
+    Please send me your improved versions.
+        PC-VAN      SCIENCE
+        NIFTY-Serve PAF01022
+        CompuServe  74050,1022
+
+**************************************************************/
+/*
+ *  lzss.c - Package for decompressing lzss compressed objects
+ *
+ *  Copyright (c) 2003 Apple Computer, Inc.
+ *
+ *  DRI: Josh de Cesare
+ */
+
+#include <sl.h>
+
+#define N         4096  /* size of ring buffer - must be power of 2 */
+#define F         18    /* upper limit for match_length */
+#define THRESHOLD 2     /* encode string into position and length
+                           if match_length is greater than this */
+#define NIL       N     /* index for root of binary search trees */
+
+int
+decompress_lzss(u_int8_t *dst, u_int8_t *src, u_int32_t srclen)
+{
+    /* ring buffer of size N, with extra F-1 bytes to aid string comparison */
+    u_int8_t text_buf[N + F - 1];
+    u_int8_t *dststart = dst;
+    u_int8_t *srcend = src + srclen;
+    int  i, j, k, r, c;
+    unsigned int flags;
+    
+    dst = dststart;
+    srcend = src + srclen;
+    for (i = 0; i < N - F; i++)
+        text_buf[i] = ' ';
+    r = N - F;
+    flags = 0;
+    for ( ; ; ) {
+        if (((flags >>= 1) & 0x100) == 0) {
+            if (src < srcend) c = *src++; else break;
+            flags = c | 0xFF00;  /* uses higher byte cleverly */
+        }   /* to count eight */
+        if (flags & 1) {
+            if (src < srcend) c = *src++; else break;
+            *dst++ = c;
+            text_buf[r++] = c;
+            r &= (N - 1);
+        } else {
+            if (src < srcend) i = *src++; else break;
+            if (src < srcend) j = *src++; else break;
+            i |= ((j & 0xF0) << 4);
+            j  =  (j & 0x0F) + THRESHOLD;
+            for (k = 0; k <= j; k++) {
+                c = text_buf[(i + k) & (N - 1)];
+                *dst++ = c;
+                text_buf[r++] = c;
+                r &= (N - 1);
+            }
+        }
+    }
+    
+    return dst - dststart;
+}
 
   // Add the Segment to the memory-map.
   sprintf(rangeName, "Kernel-%s", segCmd->segname);
   AllocateMemoryRange(rangeName, (long)vmaddr, vmsize);
+
+  if (vmsize && (strcmp(segCmd->segname, "__PRELINK") == 0))
+    gHaveKernelCache = 1;
   
   // Handle special segments first.
   
 
 long gBootMode = kBootModeNormal;
 long gBootDeviceType;
 long gBootFileType;
+char gHaveKernelCache = 0;
 char gBootDevice[256];
 char gBootFile[256];
-char gRootDir[256];
+static char gBootKernelCacheFile[512];
+static char gExtensionsSpec[4096];
+static char gCacheNameAdler[64 + sizeof(gBootFile)];
+static char *gPlatformName = gCacheNameAdler;
 
 char gTempStr[4096];
 
 static void Main(ClientInterfacePtr ciPtr)
 {
   long ret;
+  int  trycache;
+  long flags, cachetime, time;
   
   ret = InitEverything(ciPtr);
   if (ret != 0) Exit();
   DrawSplashScreen(0);
   
   while (ret == 0) {
+    trycache = (0 == (gBootMode & kBootModeSafe))
+             && (gBootKernelCacheFile[0] != 0);
+    
+    if (trycache && (gBootFileType == kBlockDeviceType)) do {
+      
+      // if we haven't found the kernel yet, don't use the cache
+      ret = GetFileInfo(NULL, gBootFile, &flags, &time);
+      if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) {
+       trycache = 0;
+       break;
+      }
+      ret = GetFileInfo(NULL, gBootKernelCacheFile, &flags, &cachetime);
+      if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)
+        || (cachetime < time)) {
+       trycache = 0;
+       break;
+      }
+      ret = GetFileInfo(gExtensionsSpec, "Extensions", &flags, &time);
+      if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeDirectory)
+       && (cachetime < time)) {
+       trycache = 0;
+       break;
+      }
+    } while (0);
+    
+    if (trycache) {
+      ret = LoadFile(gBootKernelCacheFile);
+      if (ret != -1) {
+        ret = DecodeKernel();
+        if (ret != -1) break;
+      }
+    }
     ret = LoadFile(gBootFile);
+    if (ret != -1)
+      ret = DecodeKernel();
     if (ret != -1) break;
     
     ret = GetBootPaths();
     if (ret != 0) FailToBoot(2);
   }
   
-  ret = DecodeKernel();
   if (ret != 0) FailToBoot(3);
   
-  ret = LoadDrivers(gRootDir);
-  if (ret != 0) FailToBoot(4);
+  if (!gHaveKernelCache) {
+    ret = LoadDrivers(gExtensionsSpec);
+    if (ret != 0) FailToBoot(4);
+  }
   
   DrawSplashScreen(1);
   
   long   ret, mem_base, mem_base2, size;
   CICell keyboardPH;
   char   name[32], securityMode[33];
+  long length;
+  char *compatible;
   
   // Init the OF Client Interface.
   ret = InitCI(ciPtr);
     return -1;
   }
   
+  // Get first element of root's compatible property.
+  ret = GetPackageProperty(Peer(0), "compatible", &compatible, &length);
+  if (ret != -1)
+    strcpy(gPlatformName, compatible);
+  
   // Get stdout's IH, so that the boot display can be found.
   ret = GetProp(gChosenPH, "stdout", (char *)&gStdOutIH, 4);
   if (ret == 4) gStdOutPH = InstanceToPackage(gStdOutIH);
   return ret;
 }
 
-
 static long DecodeKernel(void)
 {
   void *binary = (void *)kLoadAddr;
   long ret;
+  compressed_kernel_header * kernel_header = (compressed_kernel_header *) kLoadAddr;
+  u_int32_t size;
+  
+  if (kernel_header->signature == 'comp') {
+    if (kernel_header->compress_type != 'lzss')
+      return -1;
+    if (kernel_header->platform_name[0] && strcmp(gPlatformName, kernel_header->platform_name))
+      return -1;
+    if (kernel_header->root_path[0] && strcmp(gBootFile, kernel_header->root_path))
+      return -1;
+    
+    binary = AllocateBootXMemory(kernel_header->uncompressed_size);
+    
+    size = decompress_lzss((u_int8_t *) binary, &kernel_header->data[0], kernel_header->compressed_size);
+    if (kernel_header->uncompressed_size != size) {
+      printf("size mismatch from lzss %x\n", size);
+      return -1;
+    }
+    if (kernel_header->adler32 !=
+       Alder32(binary, kernel_header->uncompressed_size)) {
+      printf("adler mismatch\n");
+      return -1;
+    }
+  }
   
   ThinFatBinary(&binary, 0);
   
 static long GetBootPaths(void)
 {
   long ret, cnt, cnt2, cnt3, cnt4, size, partNum, bootplen, bsdplen;
+  unsigned long adler32;
   char *filePath, *buffer;
   
   if (gBootSourceNumber == -1) {
       strncpy(gBootFile, gBootDevice, cnt + 1);
       sprintf(gBootFile + cnt + 1, "%d,%s\\mach_kernel",
              partNum, ((gBootSourceNumber & 1) ? "" : "\\"));
+
+      // and the cache file name
+
+      bzero(gCacheNameAdler + 64, sizeof(gBootFile));
+      strcpy(gCacheNameAdler + 64, gBootFile);
+      adler32 = Alder32(gCacheNameAdler, sizeof(gCacheNameAdler));
+
+      strncpy(gBootKernelCacheFile, gBootDevice, cnt + 1);
+      sprintf(gBootKernelCacheFile + cnt + 1, 
+               "%d,\\System\\Library\\Caches\\com.apple.kernelcaches\\kernelcache.%08lX", partNum, adler32);
       break;
-      
+
     default:
       printf("Failed to infer Boot Device Type.\n");
       return -1;
   }
   
   // Figure out the root dir.
-  ret = ConvertFileSpec(gBootFile, gRootDir, &filePath);
+  ret = ConvertFileSpec(gBootFile, gExtensionsSpec, &filePath);
   if (ret == -1) return -1;
   
-  strcat(gRootDir, ",");
+  strcat(gExtensionsSpec, ",");
   
   // Add in any extra path to gRootDir.
   cnt = 0;
   if (cnt != 0) {
     for (cnt2 = cnt - 1; cnt2 >= 0; cnt2--) {
       if (filePath[cnt2] == '\\') {
-       strncat(gRootDir, filePath, cnt2 + 1);
+       strncat(gExtensionsSpec, filePath, cnt2 + 1);
        break;
       }
     }
   }
-  
+
+  // Figure out the extensions dir.
+  if (gBootFileType == kBlockDeviceType) {
+    cnt = strlen(gExtensionsSpec);
+    if ((cnt > 2) && (gExtensionsSpec[cnt-1] == '\\') && (gExtensionsSpec[cnt-2] == '\\'))
+       cnt--;
+    strcpy(gExtensionsSpec + cnt, "System\\Library\\");
+  }
+
   SetProp(gChosenPH, "rootpath", gBootFile, strlen(gBootFile) + 1);
   
   gBootSourceNumber++;